// MainDlg.h : interface of the CMainDlg class
//
/////////////////////////////////////////////////////////////////////////////

#pragma once

#include <sstream>
#include <iomanip>
#include "ListCtrl.h"
#include "usbcomm.h"
#include "NewSlaveWizard.h"
#include "resource.h"
#include <map>
#include <algorithm>
#include <vector>
#include "IRCReceiver.h"

extern LPCTSTR g_PP_URL;

class CHmcOptionDialog : public CDialogImpl<CHmcOptionDialog>
{
	CHyperLink          m_wndLink;

public:
	enum { IDD = IDD_HMC_SETTINGS };

	BEGIN_MSG_MAP(thisClass)
		MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
		COMMAND_ID_HANDLER(IDOK, OnOK)
		COMMAND_ID_HANDLER(IDCANCEL, OnCancel)		
		COMMAND_HANDLER_EX(IDC_CB_DOORMODEDELAY, CBN_SELCHANGE , OnDoorDelayModified)
		COMMAND_ID_HANDLER(IDC_DOORMONITORMODE, OnDoorMonitorModeOn)
		COMMAND_ID_HANDLER(IDC_BEEP_AT_TAG, OnBeepAtTagClicked)
		COMMAND_ID_HANDLER(IDC_BEEP_AT_PC, OnBeepAtPCClicked)
		COMMAND_ID_HANDLER(IDC_MOTIONDETECTMODE, OnDoorMonitorModeOff)
		COMMAND_ID_HANDLER(IDC_TEST_AUDIO, OnTestAudio)
	END_MSG_MAP()

	LRESULT OnTestAudio(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		//DWORD oldvol;
		//waveOutGetVolume(NULL, &oldvol);
		WORD vol = m_vol.GetPos()*256;
		waveOutSetVolume(NULL, vol | vol<<16);
		PlaySound(_T("SystemAsterisk"), NULL, SND_ALIAS|SND_ASYNC);
		//waveOutSetVolume(NULL, oldvol);
		return 0;		
	}

	CSlaveProfile* m_profile;
	CListSlaveData::EmailSet* m_known_emailaddrs;
	CHmcOptionDialog(CSlaveProfile* profile, CListSlaveData::EmailSet* known_emailaddrs) : m_profile(profile), m_known_emailaddrs(known_emailaddrs)
	{	
		m_needUpdateTag=false;
	}
	LRESULT OnDoorDelayModified(UINT uNotifyCode, int nID, CWindow wndCtl )
	{
		m_beep_at_tag_autostop.EnableWindow(m_cbDoorDelay.GetCurSel()==0 && m_beep_at_tag.GetCheck()==BST_CHECKED);
		return 0;
	}

	bool m_needUpdateTag;
	bool m_applyToAll;
	LRESULT OnOK(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		CString emailaddr;
		m_cbEmailaddr.GetWindowText(emailaddr);
		m_known_emailaddrs->insert(emailaddr);
		m_profile->m_hmcOption.email = emailaddr;
		m_profile->m_hmcOption.send_email = (m_emailme.GetCheck() == BST_CHECKED);
		m_profile->m_hmcOption.beep_pc= (m_beep_at_pc.GetCheck() == BST_CHECKED);
		m_profile->m_hmcOption.beep_pc_loop = (m_beep_at_pc_loop.GetCheck() == BST_CHECKED);
		m_profile->m_hmcOption.beep_pc_vol = m_vol.GetPos();

		m_profile->m_hmcOption.beep_tag_autostop = (m_beep_at_tag_autostop.GetCheck() == BST_CHECKED);
		
		if( (m_doormode.GetCheck() == BST_CHECKED) != m_profile->m_hmcOption.door_mode){
			m_profile->m_hmcOption.door_mode= (m_doormode.GetCheck() == BST_CHECKED);
			m_needUpdateTag = true;
		}
		
		/*if(m_profile->m_hmcOption.beep_tag !=  (m_beep_at_tag.GetCheck() == BST_CHECKED)){
			m_profile->m_hmcOption.beep_tag= (m_beep_at_tag.GetCheck() == BST_CHECKED);
			m_needUpdateTag=true;
		}*/
		m_profile->m_hmcOption.beep_tag= (m_beep_at_tag.GetCheck() == BST_CHECKED);


		if(m_sensitivity.GetPos() != m_profile->m_hmcOption.sensitivity){
			m_profile->m_hmcOption.sensitivity = m_sensitivity.GetPos();
			m_needUpdateTag=true;
		}
		if(m_profile->m_hmcOption.interval != m_cbInterval.GetCurSel()+1){
			m_profile->m_hmcOption.interval = m_cbInterval.GetCurSel()+1;
			m_needUpdateTag=true;
		}
		
		m_profile->m_hmcOption.door_mode_delay = m_cbDoorDelay.GetCurSel();
		m_profile->m_hmcOption.door_mode_angle = m_cbAngle.GetCurSel();

		m_applyToAll = (::SendMessage(GetDlgItem(IDC_CHECK_APPLYALL), BM_GETCHECK, 0, 0) == BST_CHECKED);
		EndDialog(1);
		return 0;
	}

	LRESULT OnDoorMonitorModeOff(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		this->m_cbDoorDelay.ShowWindow(SW_HIDE);		
		m_cbAngle.EnableWindow(FALSE);
		GetDlgItem(IDC_STATIC_ANGLE).EnableWindow(FALSE);
		GetDlgItem(IDC_STATIC_GROUP).SetWindowText(_T("When Tag Movement is Detected"));
		m_beep_at_tag_autostop.EnableWindow(FALSE);
		return 0;
	}
	
	LRESULT OnDoorMonitorModeOn(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		m_cbAngle.EnableWindow(TRUE);
		GetDlgItem(IDC_STATIC_GROUP).SetWindowText(_T(""));
		GetDlgItem(IDC_STATIC_ANGLE).EnableWindow(TRUE);
		this->m_cbDoorDelay.ShowWindow(SW_SHOWNORMAL);
		m_beep_at_tag_autostop.EnableWindow(m_beep_at_tag.GetCheck()==BST_CHECKED && m_cbDoorDelay.GetCurSel()==0);
		return 0;
	}
	LRESULT OnBeepAtTagClicked(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		m_beep_at_tag_autostop.EnableWindow(m_beep_at_tag.GetCheck() == BST_CHECKED);
		return 0;
	}
	LRESULT OnBeepAtPCClicked(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		bool enable= m_beep_at_pc.GetCheck() == BST_CHECKED;
		m_beep_at_pc_loop.EnableWindow(enable);
		m_vol.EnableWindow(enable);
		GetDlgItem(IDC_TEST_AUDIO).EnableWindow(enable);
		return 0;
	}
	LRESULT OnCancel(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		EndDialog(0);
		return 0;
	}
	CComboBox m_cbEmailaddr, m_cbInterval, m_cbDoorDelay, m_cbAngle;
	CButton m_emailme, m_beep_at_pc, m_beep_at_pc_loop, m_beep_at_tag, m_beep_at_tag_autostop, m_doormode, m_motionmode;
	CTrackBarCtrl m_sensitivity, m_vol;

	LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
	{
		m_wndLink.SubclassWindow(GetDlgItem(IDC_PP_LINK));
		m_wndLink.SetHyperLink ( g_PP_URL );
    
		
		m_cbEmailaddr.Attach(GetDlgItem( IDC_CB_EMAILADDR) );
		for(CListSlaveData::EmailSet::iterator i = m_known_emailaddrs->begin(); i!=m_known_emailaddrs->end(); i++){	
			m_cbEmailaddr.AddString(i->GetString());
		}
		if(m_profile->m_hmcOption.email.IsEmpty()){
			if(m_profile->m_beeperOption.email.IsEmpty())
				m_cbEmailaddr.SetCurSel(0);
			else
				m_cbEmailaddr.SetWindowText(m_profile->m_beeperOption.email.GetString());
		}
		else
			m_cbEmailaddr.SetWindowText(m_profile->m_hmcOption.email.GetString());
		
		m_cbAngle.Attach(GetDlgItem( IDC_CB_ANGLE) );
		m_cbAngle.AddString(_T("5 degrees"));
		m_cbAngle.AddString(_T("10 degrees"));
		m_cbAngle.AddString(_T("15 degrees"));
		m_cbAngle.AddString(_T("22.5 degrees"));
		m_cbAngle.AddString(_T("30 degrees"));
		m_cbAngle.AddString(_T("45 degrees"));
		m_cbAngle.AddString(_T("60 degrees"));
		m_cbAngle.AddString(_T("75 degrees"));
		m_cbAngle.AddString(_T("90 degrees"));
		m_cbAngle.SetCurSel(m_profile->m_hmcOption.door_mode_angle);

		m_cbDoorDelay.Attach(GetDlgItem( IDC_CB_DOORMODEDELAY) );
		m_cbDoorDelay.AddString(_T("When door is opened, immediately"));
		m_cbDoorDelay.AddString(_T("When door has been open for 30 seconds"));
		m_cbDoorDelay.AddString(_T("When door has been open for 1 minute"));
		m_cbDoorDelay.AddString(_T("When door has been open for 2 minutes"));
		m_cbDoorDelay.AddString(_T("When door has been open for 5 minutes"));
		m_cbDoorDelay.AddString(_T("When door has been open for 10 minutes"));
		m_cbDoorDelay.AddString(_T("When door has been open for 15 minutes"));
		m_cbDoorDelay.AddString(_T("When door has been open for 30 minutes"));
		m_cbDoorDelay.AddString(_T("When door has been open for One Hour"));
		m_cbDoorDelay.AddString(_T("When door has been open for Two Hours"));
		m_cbDoorDelay.SetCurSel(m_profile->m_hmcOption.door_mode_delay);

		m_cbInterval.Attach(GetDlgItem( IDC_CB_RESPONSIVENESS) );
		m_cbInterval.AddString(_T("Fast (worst battery life)"));
		m_cbInterval.AddString(_T("Medium fast"));
		m_cbInterval.AddString(_T("Medium"));
		m_cbInterval.AddString(_T("Medium slow"));
		m_cbInterval.AddString(_T("Slow (best battery life)"));
		m_cbInterval.SetCurSel(m_profile->m_hmcOption.interval-1);

		m_emailme.Attach(GetDlgItem( IDC_EMAILME) );
		m_emailme.SetCheck(m_profile->m_hmcOption.send_email? BST_CHECKED : BST_UNCHECKED);

		m_beep_at_pc.Attach(GetDlgItem( IDC_BEEP_AT_PC) );
		m_beep_at_pc.SetCheck(m_profile->m_hmcOption.beep_pc? BST_CHECKED : BST_UNCHECKED);
		m_beep_at_pc_loop.Attach(GetDlgItem( IDC_REPEAT_AUDIO) );
		m_beep_at_pc_loop.SetCheck(m_profile->m_hmcOption.beep_pc_loop? BST_CHECKED : BST_UNCHECKED);

		m_beep_at_tag.Attach(GetDlgItem( IDC_BEEP_AT_TAG) );
		m_beep_at_tag.SetCheck(m_profile->m_hmcOption.beep_tag? BST_CHECKED : BST_UNCHECKED);
		
		m_beep_at_tag_autostop.Attach(GetDlgItem( IDC_BEEP_AT_TAG_AUTOSTOP) );
		m_beep_at_tag_autostop.SetCheck(m_profile->m_hmcOption.beep_tag_autostop? BST_CHECKED : BST_UNCHECKED);
		
		m_doormode.Attach(GetDlgItem( IDC_DOORMONITORMODE) );
		m_doormode.SetCheck(m_profile->m_hmcOption.door_mode ? BST_CHECKED : BST_UNCHECKED);
		
		m_motionmode.Attach(GetDlgItem( IDC_MOTIONDETECTMODE) );
		m_motionmode.SetCheck(m_profile->m_hmcOption.door_mode ? BST_UNCHECKED : BST_CHECKED);
		
		BOOL dummy;
		if(m_profile->m_hmcOption.door_mode)
			OnDoorMonitorModeOn(0,0,m_doormode, dummy);
		else
			OnDoorMonitorModeOff(0,0,m_motionmode, dummy);

		m_sensitivity.Attach(GetDlgItem( IDC_HMCSENSITIVITY_SLIDER) );
		m_sensitivity.SetRange(0,100);
		m_sensitivity.SetPos(m_profile->m_hmcOption.sensitivity);
		
		m_vol.Attach(GetDlgItem( IDC_AUDIO_VOL) );
		m_vol.SetRange(0,255);
		m_vol.SetPos(m_profile->m_hmcOption.beep_pc_vol);
		return TRUE;
	}
};


class CBeeperOptionDialog : public CDialogImpl<CBeeperOptionDialog>
{
	CHyperLink          m_wndLink;

public:
	enum { IDD = IDD_BEEPER_SETTINGS };

	BEGIN_MSG_MAP(thisClass)
		MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
		COMMAND_ID_HANDLER(IDOK, OnOK)
		COMMAND_ID_HANDLER(IDCANCEL, OnCancel)		
		COMMAND_ID_HANDLER(IDC_TEST_AUDIO, OnTestAudio)		
		COMMAND_ID_HANDLER(IDC_BEEP_AT_PC, OnBeepAtPCClicked)
	END_MSG_MAP()
	
	LRESULT OnTestAudio(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		//DWORD oldvol;
		//waveOutGetVolume(NULL, &oldvol);
		WORD vol = m_vol.GetPos()*256;
		waveOutSetVolume(NULL, vol | vol<<16);
		PlaySound(_T("SystemHand"), NULL, SND_ALIAS|SND_ASYNC);
		//waveOutSetVolume(NULL, oldvol);
		return 0;		
	}
	LRESULT OnBeepAtPCClicked(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		m_vol.EnableWindow(m_beep_at_pc.GetCheck() == BST_CHECKED);
		GetDlgItem(IDC_TEST_AUDIO).EnableWindow(m_beep_at_pc.GetCheck() == BST_CHECKED);
		return 0;
	}
	
	CTrackBarCtrl m_vol;

	CSlaveProfile* m_profile;
	CListSlaveData::EmailSet* m_known_emailaddrs;
	bool m_applyToAll;

	CBeeperOptionDialog(CSlaveProfile* profile, CListSlaveData::EmailSet* known_emailaddrs) : m_profile(profile), m_known_emailaddrs(known_emailaddrs)
	{	
	}

	LRESULT OnOK(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		CString emailaddr;
		m_cbEmailaddr.GetWindowText(emailaddr);
		m_known_emailaddrs->insert(emailaddr);
		m_profile->m_beeperOption.email = emailaddr;
		m_profile->m_beeperOption.send_email = (m_emailme.GetCheck() == BST_CHECKED);
		m_profile->m_beeperOption.beep_pc= (m_beep_at_pc.GetCheck() == BST_CHECKED);
		m_applyToAll = (::SendMessage(GetDlgItem(IDC_CHECK_APPLYALL), BM_GETCHECK, 0, 0) == BST_CHECKED);
		m_profile->m_beeperOption.beep_pc_vol = m_vol.GetPos();
		EndDialog(1);
		return 0;
	}

	LRESULT OnCancel(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		EndDialog(0);
		return 0;
	}
	CComboBox m_cbEmailaddr;
	CButton m_emailme, m_beep_at_pc;

	LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
	{
		m_vol.Attach(GetDlgItem( IDC_AUDIO_VOL) );
		m_vol.SetRange(0,255);
		m_vol.SetPos(m_profile->m_beeperOption.beep_pc_vol);

		m_wndLink.SubclassWindow(GetDlgItem(IDC_PP_LINK));
		m_wndLink.SetHyperLink ( g_PP_URL );

		m_cbEmailaddr.Attach(GetDlgItem( IDC_CB_EMAILADDR) );
		for(CListSlaveData::EmailSet::iterator i = m_known_emailaddrs->begin(); i!=m_known_emailaddrs->end(); i++){	
			m_cbEmailaddr.AddString(i->GetString());
		}
		if(m_profile->m_beeperOption.email.IsEmpty()){
			if(m_profile->m_hmcOption.email.IsEmpty())
				m_cbEmailaddr.SetCurSel(0);
			else
				m_cbEmailaddr.SetWindowText(m_profile->m_hmcOption.email.GetString());
		}
		else
			m_cbEmailaddr.SetWindowText(m_profile->m_beeperOption.email.GetString());

		m_emailme.Attach(GetDlgItem( IDC_EMAILME) );
		m_emailme.SetCheck(m_profile->m_beeperOption.send_email? BST_CHECKED : BST_UNCHECKED);

		m_beep_at_pc.Attach(GetDlgItem( IDC_BEEP_AT_PC) );
		m_beep_at_pc.SetCheck(m_profile->m_beeperOption.beep_pc? BST_CHECKED : BST_UNCHECKED);

		return TRUE;
	}
};

class CMigrationDialog : public CDialogImpl<CMigrationDialog>
{
public:
	enum { IDD = IDD_DATARATE };

	BEGIN_MSG_MAP(thisClass)
		MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
		COMMAND_ID_HANDLER(IDOK, OnOK)
		COMMAND_ID_HANDLER(IDCANCEL, OnCancel)		
		COMMAND_HANDLER_EX(IDC_NEWFREQ, CBN_SELCHANGE , OnConfigModified)
		COMMAND_HANDLER_EX(IDC_NEWDATARATE, CBN_SELCHANGE , OnConfigModified)
		COMMAND_HANDLER_EX(IDC_NEWRESPONSE, CBN_SELCHANGE , OnConfigModified)
	END_MSG_MAP()

	LRESULT OnConfigModified(UINT uNotifyCode, int nID, CWindow wndCtl )
	{
		switch(nID){
			case IDC_NEWDATARATE:
				m_newConfig.dataRate = m_cbNewDR.GetValue(); break;
			case IDC_NEWFREQ:
				m_newConfig.freq = m_cbNewFreq.GetValue(); break;
			case IDC_NEWRESPONSE:
				m_newConfig.activeInterval = m_cbNewAI.GetValue(); break;
		}
		
		GetDlgItem(IDOK).EnableWindow(!(m_newConfig==m_oldConfig));

		if(nID == IDC_NEWDATARATE){
			CString range;
			range.Format(_T("Estimated Range: %dm"), m_newConfig.EstimateRangeInMeter());
			GetDlgItem(IDC_NEWRANGEEST).SetWindowText(range);
		}
		if(nID == IDC_NEWDATARATE || nID == IDC_NEWRESPONSE){
			CString bat;
			bat.Format(_T("Estimated Battery Life: %.1f months"), m_newConfig.EstimateBatteryLifeInMonths());
			GetDlgItem(IDC_NEWBATEST).SetWindowText(bat);
		}
		return 0;
	}

	CWirelessConfig m_newConfig, m_oldConfig;
	CString m_retryText;
	
	CMigrationDialog(CWirelessConfig newConfig, CWirelessConfig oldConfig, CString retryText=_T("")) 
		: m_retryText(retryText) 
	{
		if(retryText.IsEmpty()){
			m_oldConfig=m_newConfig=newConfig;
		}else{
			m_oldConfig=oldConfig;
			m_newConfig=newConfig;
		}
	}

	LRESULT OnOK(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		EndDialog(1);
		return 0;
	}

	LRESULT OnCancel(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		EndDialog(0);
		return 0;
	}

	class CResponseComboBox : public CComboBox{
		static LPCTSTR m_texts[8];
		static U8 m_values[8];

	public:
		void SetValue(U8 value){
			for(int i=0;i<sizeof(m_values);i++){
				if(value==m_values[i])
					this->SetCurSel(i);
			}
		}
		void Attach(HWND wnd){
			CComboBox::Attach(wnd);
			this->ResetContent();
			for(int i=0;i<sizeof(m_values);i++){
				this->AddString(m_texts[i]);
			}
		}
		U8 GetValue(){
			return m_values[this->GetCurSel()];
		}
	};
	CResponseComboBox m_cbOldAI, m_cbNewAI;

	class CDataRateComboBox : public CComboBox{
		static LPCTSTR m_texts[4];
		static U8 m_values[4];
	public:
		
		void SetValue(U8 value){
			for(int i=0;i<sizeof(m_values);i++){
				if(value==m_values[i])
					this->SetCurSel(i);
			}
		}
		void Attach(HWND wnd){
			CComboBox::Attach(wnd);
			this->ResetContent();
			for(int i=0;i<sizeof(m_values);i++){
				this->AddString(m_texts[i]);
			}
		}
		U8 GetValue(){
			return m_values[this->GetCurSel()];
		}
	};
	CDataRateComboBox m_cbOldDR, m_cbNewDR;

	class CFreqComboBox : public CComboBox{
		CString m_texts[474];
		U16 m_values[474];
	public:
		void SetValue(U16 value){
			for(int i=0;i<sizeof(m_values);i++){
				if(value==m_values[i])
					this->SetCurSel(i);
			}
		}

		void Attach(HWND wnd){
			CComboBox::Attach(wnd);
			this->ResetContent();
			for(int i=0;i<474;i++){
				m_values[i]=(13+i)*8+1;
				m_texts[i].Format("%.2fMHz", 430.0+0.0025*m_values[i]);
				this->AddString(m_texts[i]);
			}
		}
		U16 GetValue(){
			return m_values[this->GetCurSel()];
		}
	};
	CFreqComboBox m_cbOldFreq, m_cbNewFreq;

	LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
	{
		m_cbOldDR.Attach(GetDlgItem(IDC_OLDDATARATE));
		m_cbOldDR.SetValue(this->m_oldConfig.dataRate);

		m_cbNewDR.Attach(GetDlgItem(IDC_NEWDATARATE));
		m_cbNewDR.SetValue(this->m_newConfig.dataRate);
		
		m_cbOldAI.Attach(GetDlgItem(IDC_OLDRESPONSE));
		m_cbOldAI.SetValue(this->m_oldConfig.activeInterval);

		m_cbNewAI.Attach(GetDlgItem(IDC_NEWRESPONSE));
		m_cbNewAI.SetValue(this->m_newConfig.activeInterval);

		m_cbOldFreq.Attach(GetDlgItem(IDC_OLDFREQ));
		m_cbOldFreq.SetValue(this->m_oldConfig.freq);

		m_cbNewFreq.Attach(GetDlgItem(IDC_NEWFREQ));
		m_cbNewFreq.SetValue(this->m_newConfig.freq);

		if(!m_retryText.IsEmpty()){
			GetDlgItem(IDOK).SetWindowText(_T("Retry Program These Tags"));
			CString inst;
			inst.Format(_T("The following tags must either complete migration or be unassociated before you can pick a different configuration:\n%s"), m_retryText);
			GetDlgItem(IDC_INSTRUCTION).SetWindowText(inst);
			m_cbNewFreq.EnableWindow(FALSE);
			m_cbNewDR.EnableWindow(FALSE);
			m_cbNewAI.EnableWindow(FALSE);
		}

		CString data;
		data.Format(_T("Estimated Range: %dm"), m_newConfig.EstimateRangeInMeter());
		GetDlgItem(IDC_NEWRANGEEST).SetWindowText(data);
		data.Format(_T("Estimated Battery Life: %.1f months"), m_newConfig.EstimateBatteryLifeInMonths());
		GetDlgItem(IDC_NEWBATEST).SetWindowText(data);
		data.Format(_T("Estimated Range: %dm"), m_oldConfig.EstimateRangeInMeter());
		GetDlgItem(IDC_OLDRANGEEST).SetWindowText(data);
		data.Format(_T("Estimated Battery Life: %.1f months"), m_oldConfig.EstimateBatteryLifeInMonths());
		GetDlgItem(IDC_OLDBATEST).SetWindowText(data);

		return TRUE;
	}

};

class CCreateAccountDialog : public CDialogImpl<CCreateAccountDialog>
{
	CHyperLink          m_wndLink;

public:
	enum { IDD = IDD_CREATEACCOUNT };

	BEGIN_MSG_MAP(thisClass)
		MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
		COMMAND_ID_HANDLER(IDOK, OnOK)
		COMMAND_ID_HANDLER(ID_LOGIN, OnLogin)
		COMMAND_ID_HANDLER(IDCANCEL, OnCancel)		
		COMMAND_HANDLER_EX(IDC_REG_EMAIL, CBN_EDITCHANGE , OnTextModified)		
		COMMAND_HANDLER_EX(IDC_REG_PWD, EN_CHANGE, OnTextModified)
	END_MSG_MAP()

	LRESULT OnTextModified(UINT uNotifyCode, int nID, HWND wndCtl )
	{
		CString email, pwd;
		GetDlgItem(IDC_REG_EMAIL).GetWindowText(email);
		GetDlgItem(IDC_REG_PWD).GetWindowText(pwd);
		bool validated = email.Find(_T("@"))!=-1 && pwd.GetLength()>2;
		GetDlgItem(IDOK).EnableWindow(validated);
		GetDlgItem(ID_LOGIN).EnableWindow(validated);
		return 0;
	}

	CListSlaveData::EmailSet* m_known_emailaddrs;
	CString& m_email;
	CString& m_pwd;

	CCreateAccountDialog(CString& email, CString& pwd, CListSlaveData::EmailSet* known_emailaddrs, bool& dontConn) 
		: m_known_emailaddrs(known_emailaddrs), m_email(email), m_pwd(pwd), m_dontConnect(dontConn)
	{	
	}

	LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		m_dontConnect= (m_cbDontConn.GetCheck() == BST_CHECKED);
		return 0;
	}
	LRESULT OnLogin(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		GetDlgItem(IDC_REG_EMAIL).GetWindowText(m_email);
		GetDlgItem(IDC_REG_PWD).GetWindowText(m_pwd);
		
		EndDialog(1);
		return 0;
	}
	LRESULT OnOK(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		CString pwd1, pwd2;
		GetDlgItem(IDC_REG_PWD).GetWindowText(pwd1);
		GetDlgItem(IDC_REG_PWD2).GetWindowText(pwd2);
		if(CString(pwd1).Compare(pwd2)!=0){
			MessageBox(_T("Password does not match"), _T("mytaglist.com Registration"), MB_ICONWARNING);
			return 0;
		}
		CString emailNew;
		GetDlgItem(IDC_REG_EMAIL).GetWindowText(emailNew);

		if(!emailNew.IsEmpty()){

			HINTERNET hSession  = InternetOpen("WirelessTagManager", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0 );
#ifdef USE_Z560
			HINTERNET hConnect = InternetConnect(hSession, _T("localhost"),Z560_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 1);
#else
			HINTERNET hConnect = InternetConnect(hSession, _T("www.mytaglist.com"),INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 1);
#endif
			HINTERNET hRequest = HttpOpenRequest(hConnect, "POST", 
				m_email.IsEmpty() ? _T("/taglistManager.asmx/CreateAccount") : _T("/taglistManager.asmx/UpdateAccount"), 
				NULL, NULL, NULL, INTERNET_FLAG_NO_UI, 1);

			static TCHAR hdrs[] =_T("Content-Type: application/json; charset=utf-8");
			CString data;
			if(m_email.IsEmpty()){
				data.Format(_T("{email:'%s',password:'%s'}"), emailNew.GetString(), pwd1.GetString());
			}else{
				data.Format(_T("{email:'%s',password:'%s',newEmail:'%s',newPassword:'%s'}"), 
					m_email.GetString(), m_pwd.GetString(), emailNew.GetString(), pwd1.GetString());
			}
			if(!HttpSendRequest(hRequest, hdrs, strlen(hdrs), data.GetBuffer(), data.GetLength())){
				MessageBox(CCaoGadgetsDotCom::WindowsErrorMessage(GetLastError()), _T("Error connecting to server"), MB_ICONWARNING);
				return 0;
			}

			TCHAR szStatusCode[32];
			DWORD dwInfoSize = 32;
			HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE, szStatusCode, &dwInfoSize, NULL);
			long nStatusCode = _ttol(szStatusCode);

			char szData[1024];
			DWORD dwSize;
			::InternetReadFile(hRequest, (LPVOID)szData, 1024, &dwSize);
			szData[dwSize]=0;

			if(nStatusCode !=200 && nStatusCode !=304){
				CString msg;
				msg.Format(_T("%s\n(Http Status %d)"), szData, nStatusCode);
				MessageBox(msg, _T("Server returned error"), MB_ICONWARNING);
				return 0;
			}
		}

		m_email = emailNew;
		m_pwd = pwd1;

		EndDialog(1);
		return 0;
	}

	LRESULT OnCancel(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		EndDialog(0);
		return 0;
	}

	CComboBox m_cbEmailaddr;
	bool& m_dontConnect;
	CButton m_cbDontConn;

	LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
	{
		m_wndLink.SubclassWindow(GetDlgItem(IDC_PP_LINK));
		m_wndLink.SetHyperLink ( g_PP_URL );

		m_cbDontConn.Attach(GetDlgItem(IDC_DONTSHOW));
		m_cbDontConn.SetCheck(m_dontConnect? BST_CHECKED:BST_UNCHECKED);

		m_cbEmailaddr.Attach(GetDlgItem( IDC_REG_EMAIL) );
		for(CListSlaveData::EmailSet::iterator i = m_known_emailaddrs->begin(); i!=m_known_emailaddrs->end(); i++)
		{	
			m_cbEmailaddr.AddString(i->GetString());
		}
		if(m_email.IsEmpty()){
			m_cbEmailaddr.SetCurSel(0);
		}else{
			m_cbEmailaddr.SetWindowText(m_email);
		}
		GetDlgItem(IDC_REG_PWD).SetWindowText(m_pwd);
		GetDlgItem(IDC_REG_PWD2).SetWindowText(m_pwd);

		if(!m_email.IsEmpty())
		{
			GetDlgItem(IDC_INSTRUCTION).SetWindowText(_T("Update your mytaglist.com account email or password here."));
			GetDlgItem(IDOK).SetWindowText(_T("Update Account"));
		}
		m_cbDontConn.SetWindowText(_T("&Don't connect to mytaglist.com."));

		OnTextModified(EN_CHANGE, IDC_REG_PWD,0);
		return TRUE;
	}
};

#define CMD_MULTI_ONCE  10

class CMainDlg : public CDialogImpl<CMainDlg>
{
public:
	CMainDlg(CString ini_fn) : m_waitCursor(false), m_list(ini_fn)
	{
		m_list.RegisterClass();
		m_outstandingQuery=0;
	}
	
	enum { IDD = IDD_MAINDLG };

	BEGIN_MSG_MAP(CMainDlg)
		MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
		MESSAGE_HANDLER(WM_SIZE, OnResize)
		
		MESSAGE_HANDLER(WM_DEVICECHANGE, OnDeviceChange)
		MESSAGE_HANDLER(WM_RECEIVE_USB_PACKET, OnReceiveUsbPacket)
		MESSAGE_HANDLER(WM_RECEIVE_USB_ERROR, OnReceiveUsbError)
		MESSAGE_HANDLER(WM_WRITE_USB_ERROR, OnWriteUsbError)
		MESSAGE_HANDLER(WM_RBUTTONUP, OnMouseRighClick)
		MESSAGE_HANDLER(WM_IRC_CMD, OnIRCCmd)
		
		//MESSAGE_HANDLER(WM_QUIT_FOR_UPDATE, OnWmQuitForUpdate)
		
		//MESSAGE_HANDLER(WM_TIMER, OnTimer)
		MSG_WM_EXITSIZEMOVE(OnExitSizeMove)

		MSG_WM_TIMER(OnTimer)
		COMMAND_ID_HANDLER(IDC_QUIT_FOR_UPDATE, OnQuitForUpdate)
		COMMAND_ID_HANDLER(IDOK, OnOK)
		COMMAND_ID_HANDLER(IDCANCEL, OnCancel)
		COMMAND_ID_HANDLER(IDC_UPDATE_STAT, OnUpdateStat)
		COMMAND_ID_HANDLER(IDC_ADDNEW, OnAddNewSlave)
		COMMAND_ID_HANDLER(IDC_CHECK_RECEIVE_EVENTS, OnCheckReceiveEvents)
		
		MESSAGE_HANDLER(WM_SYSCOMMAND, OnSysCmd)
		//NOTIFY_HANDLER_EX(IDC_USERLIST, LCN_SELECTED, OnUserListSelected)
		NOTIFY_HANDLER_EX(IDC_USERLIST, LCN_MODIFIED, OnUserModified)
		NOTIFY_HANDLER_EX(IDC_USERLIST, LCN_SPACEKEY, OnUserListSpaceKey)
		NOTIFY_HANDLER_EX(IDC_USERLIST, LCN_LEFTCLICK, OnUserListLeftClick)
		NOTIFY_HANDLER_EX(IDC_USERLIST, LCN_RIGHTCLICK, OnUserListRightClick)
		COMMAND_HANDLER_EX(IDC_COMBO_INTERVAL, CBN_SELCHANGE , OnIntervalModified)
		REFLECT_NOTIFICATIONS()
	END_MSG_MAP()

protected:
	CListSlaveData m_list;
	CComboBox m_cbUpdateInterval;
	CButton m_btnAutoOpen;
	CUsbComm m_usb;

	virtual void OnExitSizeMove(){
		m_list.ResetScrollBars();
	}
	
	LRESULT OnReceiveUsbPacket(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
	{
		UsbPacket_IN* packet = (UsbPacket_IN*)(lParam+1);
		CSlaveProfile* profile=NULL;
		int nItem;
							
		switch(packet->cmd){
			case CMD_LISTEN_FOR_ONE_EVENT:
				if(m_list.AnyArmedOrBeepingTags() || m_secUpdateInterval!=-1){
					m_usb.ResumeListening();
				}else{
					m_usb.StopListening();
				}
				
				if(packet->num_replies>0){

					MyTrace(_T("USB Packet IN CMD_LISTEN_FOR_ONE_EVENT, num_replies = %d, slave_id = %d, event_type = %d, arssi = %d\n"),  
						packet->num_replies, packet->data.event.slave_id, packet->data.event.event_type, packet->data.event.arssi);
				
					if(m_list.GetSlaveFromId(packet->data.event.slave_id, profile, nItem)){
						profile->m_Alive = true;
						profile->UpdateCommTime();
						profile->m_arssiADCout = packet->data.event.arssi; 
						profile->m_batADCout = packet->data.event.adcout;
							
						// in the process of arming
						if(packet->data.event.event_type == EVENT_COMPASS_READING_PER_REQUEST && !profile->m_hmc_rearm)
						{
							profile->m_tagType = CSlaveProfile::TagType::tagHmcArmed;
							profile->m_hmcOption.az_x = (short)(packet->data.event.compass_reading.x_hb << 8 | packet->data.event.compass_reading.x_lb);
							profile->m_hmcOption.az_y = (short)(packet->data.event.compass_reading.y_hb << 8 | packet->data.event.compass_reading.y_lb);
							profile->m_hmcOption.az_z = (short)(packet->data.event.compass_reading.z_hb << 8 | packet->data.event.compass_reading.z_lb);
							profile->m_hmcOption.door_mode=true;
						}
						else if(packet->data.event.event_type == EVENT_SLAVE_POSTBACK)
						{
								
							m_list.InvalidateItem(nItem);
						}
						else if(packet->data.event.event_type == EVENT_BEEP_STOPPED)
						{
							profile->m_Beeping=false;

							m_list.InvalidateItem(nItem);
						}
						else if(
							(packet->data.event.event_type == EVENT_COMPASS_READING || 
							packet->data.event.event_type == EVENT_COMPASS_READING_PER_REQUEST && profile->m_hmc_rearm)
							){
							// analyze using sensitivity setting, set fired flag, invalidate, sound wave file, 

#ifdef _DEBUG
							CString t; t.Format(_T("EVENT_COMPASS_READING X=%hd, Y=%hd, Z=%hd\n"),  
								packet->data.event.compass_reading.x_hb << 8 | packet->data.event.compass_reading.x_lb, 
								packet->data.event.compass_reading.y_hb << 8 | packet->data.event.compass_reading.y_lb, 
								packet->data.event.compass_reading.z_hb << 8 | packet->data.event.compass_reading.z_lb);
							
							this->SetWindowText(t);
							MyTrace(t);
#endif
							bool state_changed = false;
							if(profile->m_hmcOption.door_mode){
								double x = (short)(packet->data.event.compass_reading.x_hb << 8 | packet->data.event.compass_reading.x_lb);
								double y = (short)(packet->data.event.compass_reading.y_hb << 8 | packet->data.event.compass_reading.y_lb); 
								double z = (short)(packet->data.event.compass_reading.z_hb << 8 | packet->data.event.compass_reading.z_lb);
								double ang_diff = acos( (x*profile->m_hmcOption.az_x + y*profile->m_hmcOption.az_y + z*profile->m_hmcOption.az_z) / 
									( sqrt(x*x + y*y + z*z) * 
										sqrt(profile->m_hmcOption.az_x*profile->m_hmcOption.az_x + 
												profile->m_hmcOption.az_y*profile->m_hmcOption.az_y + 
												profile->m_hmcOption.az_z*profile->m_hmcOption.az_z)));
								if(ang_diff > profile->m_hmcOption.DoorModeAngleInRadian() ){
									state_changed = !profile->m_fired;
									profile->m_fired=true;
								}else{
									state_changed = profile->m_fired;
									profile->m_fired=false;
								}
							}else{
								state_changed = !profile->m_fired;
								profile->m_fired=true;
							}

							if(state_changed){
								m_list.InvalidateItem(nItem);							
								if((profile->m_hmcOption.beep_tag_autostop || profile->m_hmcOption.door_mode_delay>0) && !profile->m_fired){
									
									KillTimer(100+profile->m_ID);

									if(profile->m_hmcOption.beep_tag){
										UsbPacket_OUT po;
										ZeroMemory(&po, sizeof(po));
										po.slave_id = profile->m_ID;
										po.cmd = CMD_BEEP_STOP;
										sendCmd(po, !profile->m_migrationPending);
										m_list.StartAnimate(nItem);
									}
								}
								if(profile->m_fired){
									if(!profile->m_hmcOption.door_mode || profile->m_hmcOption.door_mode_delay==0){
										DoMotionSensorNotify(profile, nItem);
									}
									else{
										SetTimer(100+profile->m_ID, profile->m_hmcOption.DoorModeDelayInSeconds()*1000);
									}
								}else{
									PlaySound(NULL, NULL, SND_ASYNC);
								}
							}
						}

						if(!m_dontConn){
							CCaoGadgetsDotCom* web = new CCaoGadgetsDotCom();
							web->ManagerUpdate(m_accEmail, m_accPwd, *profile, CMD_LISTEN_FOR_ONE_EVENT);
						}

					}
				}
				break;

			case CMD_ARM_MOTION_SENSOR:
				if(m_list.GetSlaveFromId(packet->data.replies[0].slave_id, profile, nItem)){
					m_list.StopAnimation(nItem);
					
					if(packet->data.replies[0].flags == REPLY_OK){
						profile->m_Alive = true;
						profile->m_arssiADCout = profile->m_Alive ? packet->data.replies[0].arssi : 0;
						profile->UpdateCommTime();
						profile->m_batADCout = packet->data.replies[0].adcout;
					}
					
					if(profile->m_tagType == CSlaveProfile::tagHmcArmed)
					{
						profile->m_hmc_rearm=true;
					}
					else{
						profile->m_hmc_rearm=false;
						profile->m_tagType = CSlaveProfile::tagHmcArmed;
					}

					if(!m_usb.IsListening()){
						m_usb.StartListening();
					}

					profile->WriteIni(nItem,m_list.ini_fn);

					if(!m_dontConn){
						CCaoGadgetsDotCom* web = new CCaoGadgetsDotCom();
						web->ManagerUpdate(m_accEmail, m_accPwd, *profile, packet->cmd);

						/*if(CCaoGadgetsDotCom::m_ircReceiver.SendReply(profile->m_ID, packet->cmd, _T("AsyncUpdate"))){
							::PostMessage(m_hwndStatus, SB_SETTEXT, (255 ), (LPARAM)_T("Armed a motion sensor via command from mytaglist.com"));
						}*/
					}

					m_list.InvalidateItem(nItem);
					m_list.FlushIni();
				}
				break;

			case CMD_DISARM_MOTION_SENSOR:
				if(m_list.GetSlaveFromId(packet->data.replies[0].slave_id, profile, nItem)){
					m_list.StopAnimation(nItem);

					if(packet->data.replies[0].flags == REPLY_OK){
						profile->m_Alive = true;
						profile->m_arssiADCout = profile->m_Alive ? packet->data.replies[0].arssi : 0;
						
						profile->UpdateCommTime();
						profile->m_batADCout = packet->data.replies[0].adcout;			
						profile->WriteIni(nItem,m_list.ini_fn);
					}
					profile->m_tagType = CSlaveProfile::tagHmc;
					profile->m_fired=false;

					if(!m_dontConn){
						CCaoGadgetsDotCom* web = new CCaoGadgetsDotCom();
						web->ManagerUpdate(m_accEmail, m_accPwd, *profile, packet->cmd);
						
						/*if(CCaoGadgetsDotCom::m_ircReceiver.SendReply(profile->m_ID, packet->cmd, _T("AsyncUpdate"))){
							::PostMessage(m_hwndStatus, SB_SETTEXT, (255 ), (LPARAM)_T("Disrmed a motion sensor via command from mytaglist.com"));
						}*/
					}
					m_list.InvalidateItem(nItem);
					m_list.FlushIni();
				}
				break;
			
			case CMD_MIGRATE_SLAVE_FLASH:
				if(m_list.GetSlaveFromId(packet->data.replies[0].slave_id, profile, nItem)){
					m_list.StopAnimation(nItem);

					if(packet->data.replies[0].flags == REPLY_OK){
						profile->m_Alive = true; 
						profile->m_arssiADCout = profile->m_Alive ? packet->data.replies[0].arssi : 0;	
						profile->m_migrationPending=false;
						profile->UpdateCommTime();
						profile->m_batADCout = packet->data.replies[0].adcout;
					}else{
						profile->m_migrationPending=true;
					}

					profile->WriteIni(nItem,m_list.ini_fn);

					if(!m_dontConn){
						CCaoGadgetsDotCom* web = new CCaoGadgetsDotCom();
						web->ManagerUpdate(m_accEmail, m_accPwd, *profile, packet->cmd);
					}
					m_list.InvalidateItem(nItem);
					m_list.FlushIni();
				}

				break;

			case CMD_WRITE_SLAVE_FLASH:
				m_waitCursor.Restore();

				if(m_list.GetSlaveFromId(packet->data.replies[0].slave_id, profile, nItem))
					m_list.StopAnimation(nItem);

				m_usb.turn_off_radio();
				if(packet->num_replies==0)
				{
					if(IDYES==MessageBox( _T("Could not receive response from the tag; Press 'Yes' if the tag has beeped and the light started flashing, Press 'No' if nothing happened. "),
						_T("Oops..."), MB_ICONWARNING|MB_YESNO))
					{
						if(m_list.GetSlaveFromId(packet->data.replies[0].slave_id, profile, nItem)){
							profile->WriteIni(nItem, m_list.ini_fn, true);
							m_list.DeleteItem(nItem);
							m_list.FlushIni();
						}
					}else
					{
						MessageBox( _T("Recommend changing to a new battery and try again. "), _T("Wireless Tag Manager"), MB_ICONINFORMATION|MB_OK);
					}
				}
				else if(packet->data.replies[0].flags != REPLY_OK)
				{
					MessageBox( _T("Tag is	not programmable."), _T("Error"), MB_ICONERROR);
				}
				else
				{
					if(m_list.GetSlaveFromId(packet->data.replies[0].slave_id, profile, nItem)){
						profile->WriteIni(nItem, m_list.ini_fn, true);
						m_list.DeleteItem(nItem);
						m_list.FlushIni();
					}
				}
				break;


			case CMD_BEEP:
			case CMD_BEEP_FOR_DURATION:
			case CMD_BEEP_UNTIL_MOVED:
				if(m_list.GetSlaveFromId(packet->data.replies[0].slave_id, profile, nItem)){
					m_list.StopAnimation(nItem);

					if(!m_usb.IsListening())
						m_usb.StartListening();
					
					if(packet->data.replies[0].flags == REPLY_OK){
						profile->m_Alive = true;
						profile->m_arssiADCout = profile->m_Alive ? packet->data.replies[0].arssi : 0;						
						profile->m_batADCout = packet->data.replies[0].adcout;										
						profile->UpdateCommTime();
					}
					profile->m_Beeping=true;
					profile->WriteIni(nItem,m_list.ini_fn);
					
					if(!m_dontConn){
						CCaoGadgetsDotCom* web = new CCaoGadgetsDotCom();
						web->ManagerUpdate(m_accEmail, m_accPwd, *profile, packet->cmd);

						/*if(CCaoGadgetsDotCom::m_ircReceiver.SendReply(profile->m_ID, packet->cmd, _T("AsyncUpdate"))){
							::PostMessage(m_hwndStatus, SB_SETTEXT, (255 ), (LPARAM)_T("Beeped a tag via command from mytaglist.com"));
						}*/
					}
					
					m_list.InvalidateItem(nItem);
					//m_list.FlushIni();
					
				}
				break;
			case CMD_BEEP_STOP:
				if(m_list.GetSlaveFromId(packet->data.replies[0].slave_id, profile, nItem)){

					MyTrace(_T("USB Packet IN CMD_BEEP_STOP, slave_id = %d\n"),  profile->m_ID);

					m_list.StopAnimation(nItem);
					
					if(packet->data.replies[0].flags == REPLY_OK){
						profile->m_Alive = true;
						profile->m_arssiADCout = profile->m_Alive ? packet->data.replies[0].arssi : 0;
						profile->m_batADCout = packet->data.replies[0].adcout;										
						profile->UpdateCommTime();
					}

					profile->m_Beeping=false;						
					profile->WriteIni(nItem,m_list.ini_fn);

					if(!m_dontConn){
						CCaoGadgetsDotCom* web = new CCaoGadgetsDotCom();
						web->ManagerUpdate(m_accEmail, m_accPwd, *profile, packet->cmd);

						/*if(CCaoGadgetsDotCom::m_ircReceiver.SendReply(profile->m_ID, packet->cmd, _T("AsyncUpdate"))){
							::PostMessage(m_hwndStatus, SB_SETTEXT, (255 ), (LPARAM)_T("Stopped beep a tag via command from mytaglist.com"));
						}*/
					}

					m_list.InvalidateItem(nItem);
					//m_list.FlushIni();
				}
				break;

			case CMD_QUERY:
			case CMD_SET_SLAVE_POSTBACK_INTERVAL:
				if(m_list.GetSlaveFromId(packet->data.replies[0].slave_id, profile, nItem)){

					MyTrace(_T("USB Packet IN cmd id = %d, slave_id = %d\n"),  packet->cmd, profile->m_ID);

					m_list.StopAnimation(nItem);			

					if(packet->data.replies[0].flags != REPLY_NOT_RECEIVED)
					{			
						
						profile->m_Alive = true;
						profile->m_arssiADCout = profile->m_Alive ? packet->data.replies[0].arssi : 0;
					
						profile->m_batADCout = packet->data.replies[0].adcout;										
						profile->UpdateCommTime();
						profile->WriteIni(nItem,m_list.ini_fn);

						if(!m_dontConn){
							CCaoGadgetsDotCom* web = new CCaoGadgetsDotCom();
							web->ManagerUpdate(m_accEmail, m_accPwd, *profile, packet->cmd);
						}
						m_list.InvalidateItem(nItem);
					
					}else{
						
						/*UsbPacket_OUT po;
						ZeroMemory(&po, sizeof(po));
						po.cmd = packet->cmd;
						po.slave_id=packet->data.replies[0].slave_id;
						this->sendCmd(po, !profile->m_migrationPending);
						*/
					
					}//m_list.FlushIni();
				}
				break;

			case CMD_QUERY|CMD_MULTIPLE_MASK:
			case CMD_SET_SLAVE_POSTBACK_INTERVAL|CMD_MULTIPLE_MASK:
			case CMD_DISARM_MOTION_SENSOR|CMD_MULTIPLE_MASK:
			case CMD_ARM_MOTION_SENSOR|CMD_MULTIPLE_MASK:
			case CMD_BEEP_STOP|CMD_MULTIPLE_MASK:
			
				MyTrace(_T("USB Packet IN MULTI cmd = 0x%x, num_replies = %d, expected = %d, id_begin=%d, id_end=%d\n"),  
						packet->cmd, packet->num_replies, packet->num_replies_expected, packet->data.replies[0].slave_id, 
						packet->data.replies[packet->num_replies_expected-1].slave_id);

				UsbPacket_OUT original_packet = m_cmdMultiplePacket.back();
				m_cmdMultiplePacket.pop_back();
				original_packet.cmd &= ~CMD_MULTIPLE_MASK;

				for(int i=0;i<packet->num_replies_expected;i++){
					
					if(m_list.GetSlaveFromId(packet->data.replies[i].slave_id, profile, nItem)){

						
						if(packet->data.replies[i].flags == REPLY_NOT_RECEIVED){							
							original_packet.slave_id = packet->data.replies[i].slave_id;

							MyTrace(_T("Resending cmd id = %d to slave %d\n"), original_packet.cmd, original_packet.slave_id);
							sendCmd(original_packet, !profile->m_migrationPending);
							//m_list.StartAnimate(nItem);
							continue;
						}

						m_list.StopAnimation(nItem);			

						profile->m_Alive = true;

						profile->m_arssiADCout = packet->data.replies[i].arssi;
						profile->m_batADCout = packet->data.replies[i].adcout;		
						if( (packet->cmd  == (CMD_MULTIPLE_MASK|CMD_ARM_MOTION_SENSOR) )
							&& profile->m_tagType==CSlaveProfile::tagHmc ){
								if(!m_usb.IsListening()){
									m_usb.StartListening();
								}
								profile->m_tagType = CSlaveProfile::tagHmcArmed;
							}
						else if(packet->cmd == (CMD_MULTIPLE_MASK|CMD_DISARM_MOTION_SENSOR) && (profile->m_tagType==CSlaveProfile::tagHmcArmed))
						{
							profile->m_tagType = CSlaveProfile::tagHmc;
						}
						profile->UpdateCommTime();


						//profile->WriteIni(nItem,m_list.ini_fn);
					}
				}
				
				if(!m_dontConn){
					CCaoGadgetsDotCom* web = new CCaoGadgetsDotCom();
					web->ManagerLoginOrUpdateAll(false, m_accEmail, m_accPwd, m_list.m_aSlaves, packet->cmd);
				}

				m_outstandingQuery--;
				if(m_outstandingQuery==0){
					m_list.StopAnimateAll();

					/*if(!m_dontConn){
						CCaoGadgetsDotCom* web = new CCaoGadgetsDotCom();
						web->ManagerLoginOrUpdateAll(false, m_accEmail, m_accPwd, m_list.m_aSlaves, packet->cmd);
					}*/
				}
				
				//m_list.Invalidate();
				//m_list.FlushIni();

				break;
		}

		delete  (BYTE*)lParam;
		return 0;
	}

	void DoMotionSensorNotify(CSlaveProfile* profile, int nItem)
	{
		if(profile->m_hmcOption.beep_tag && profile->m_fired){
			UsbPacket_OUT po;
			ZeroMemory(&po, sizeof(po));
			po.slave_id = profile->m_ID;
			po.cmd = CMD_BEEP_FOR_DURATION;
			po.extra.beep_for_duration.beep_duration = 30;
			sendCmd(po, !profile->m_migrationPending);
			profile->m_Beeping=true;
			m_list.StartAnimate(nItem);
		}
		if(profile->m_hmcOption.beep_pc){
			
			waveOutSetVolume(NULL, profile->m_hmcOption.beep_pc_vol<<8 | profile->m_hmcOption.beep_pc_vol<<24);
			
			PlaySound(_T("SystemAsterisk"), NULL, SND_ALIAS|SND_ASYNC| (profile->m_hmcOption.beep_pc_loop?SND_LOOP:0));
			//m_list.SelectItem(nItem);
			SetForegroundWindow(m_hWnd);
		}
		if(profile->m_hmcOption.send_email){
			CCaoGadgetsDotCom *web = new CCaoGadgetsDotCom();
			web->SendNotifyEmail(profile->m_hmcOption.email, profile->m_Name, 
				profile->m_hmcOption.door_mode ? (profile->m_fired ? 100+profile->m_hmcOption.DoorModeDelayInSeconds() : 3) : 2);
		}
	}

	LRESULT OnReceiveUsbError(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
	{
		LPVOID lpMsgBuf;

		if(lParam != 1167){

			FormatMessage(
				FORMAT_MESSAGE_ALLOCATE_BUFFER | 
				FORMAT_MESSAGE_FROM_SYSTEM,
				NULL,
				lParam,
				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
				(LPTSTR) &lpMsgBuf,
				0, NULL );

			CString msg;
			msg.Format(_T("USB read failed with error %d: %s"), lParam, lpMsgBuf); 
			MessageBox(msg);

			LocalFree(lpMsgBuf);
		}

		m_usb.detachDevice();
		PostMessage(WM_DEVICECHANGE, DBT_CONFIGCHANGED, 0);  // let's retry find another stick

		return 0;
	}
	LRESULT OnIRCCmd(UINT /*uMsg*/, WPARAM wpara, LPARAM cmd_option, BOOL& /*bHandled*/)
	{
		CSlaveProfile* profile=NULL; int nItem=0; 
		int tag_id=LOWORD(wpara), tag_cmd=HIWORD(wpara); 

		
		if(!m_usb.IsAttached()){
			CCaoGadgetsDotCom::m_ircReceiver.SendReply(tag_id, tag_cmd, _T("Not Attached"));
			return 0;
		}

		if(tag_id!=0xff){
			m_list.GetSlaveFromId(tag_id, profile, nItem);
		}
		bool handled=false;
		switch(tag_cmd){
			case CMD_QUERY|CMD_MULTIPLE_MASK:
				UsbPacket_OUT packet;
				ZeroMemory(&packet, sizeof(packet));
				packet.cmd = CMD_QUERY;
				sendCmdToAll(packet);
				handled=true;
				break;			

			case CMD_BEEP_UNTIL_MOVED:
				if(profile!=NULL){HandleCtxMenu(17, profile, nItem); handled=true; }
				break;
			case CMD_BEEP:
				if(profile!=NULL){HandleCtxMenu(12, profile, nItem);	handled=true; }
				break;
			case CMD_BEEP_FOR_DURATION:
				if(profile!=NULL){HandleCtxMenu(cmd_option, profile, nItem); handled=true; }
				break;
			case CMD_BEEP_STOP:
				if(profile!=NULL){HandleCtxMenu(13, profile, nItem); handled=true; }
				break;
			case CMD_BEEP_STOP|CMD_MULTIPLE_MASK:
				HandleCtxMenu(16);
				handled=true;
				break;
			case CMD_WEB_RESET:
				if(profile!=NULL){HandleCtxMenu(7, profile, nItem); handled=true; }
				break;
			case CMD_ARM_MOTION_SENSOR:
				if(profile!=NULL){HandleCtxMenu(4, profile, nItem); handled=true; }
				break;
			case CMD_ARM_MOTION_SENSOR|CMD_MULTIPLE_MASK:
				HandleCtxMenu(14);
				handled=true;
				break;
			case CMD_DISARM_MOTION_SENSOR:
				if(profile!=NULL){HandleCtxMenu(5, profile, nItem); handled=true; }
				break;
			case CMD_DISARM_MOTION_SENSOR|CMD_MULTIPLE_MASK:
				HandleCtxMenu(15);
				handled=true;
				break;			
		}

		if(!handled){
			CCaoGadgetsDotCom::m_ircReceiver.SendReply(tag_id, tag_cmd, _T("Err"));
		}
		return 0;
	}
	LRESULT OnMouseRighClick(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		CMenu menu; 
		menu.CreatePopupMenu();
		menu.AppendMenu(MF_STRING, ID_FILE_NEW, _T("Create mytaglist.com &Account"));
		menu.AppendMenu(MF_STRING, ID_FILE_OPEN, _T("Update mytaglist.com &Account"));
		menu.AppendMenu(MF_SEPARATOR);
		
		menu.AppendMenu(MF_STRING, ID_APP_ABOUT, _T("&Version Info"));
		menu.AppendMenu(MF_STRING, ID_FILE_UPDATE, _T("Check &Update"));
		menu.AppendMenu(MF_SEPARATOR);
		menu.AppendMenu(MF_STRING, ID_FILE_SAVE, _T("Migrate &Wireless Configuration"));
		
		POINT cursorPos;
		::GetCursorPos(&cursorPos);
		int cmdid = menu.TrackPopupMenu(TPM_LEFTALIGN|TPM_RETURNCMD|TPM_NONOTIFY, cursorPos.x,cursorPos.y, m_hWnd); 
		if(cmdid)
			return this->OnSysCmd(0, cmdid,0, bHandled);
		else
			return 0;
	}
	LRESULT OnWriteUsbError(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
	{
		LPVOID lpMsgBuf;
		
		if(lParam != 1167){

			FormatMessage(
				FORMAT_MESSAGE_ALLOCATE_BUFFER | 
				FORMAT_MESSAGE_FROM_SYSTEM,
				NULL,
				lParam,
				MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
				(LPTSTR) &lpMsgBuf,
				0, NULL );

			CString msg;
			msg.Format(_T("USB write failed with error %d: %s"), lParam, lpMsgBuf); 
			MessageBox(msg);

			LocalFree(lpMsgBuf);
		}

		return 0;
	}

	void EnableWindow(BOOL en){
		::EnableWindow(m_hwndStatus, en);
		GetDlgItem(IDC_UPDATE_STAT).EnableWindow(en);
		GetDlgItem(IDC_ADDNEW).EnableWindow(en);
		m_list.EnableWindow(en);
		m_cbUpdateInterval.EnableWindow(en);
		//m_btnAutoOpen.EnableWindow(en);
		m_list.Invalidate();
	}

	LRESULT OnDeviceChange(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
	{
		if((int)wParam == DBT_CONFIGCHANGED || 
			((int)wParam == DBT_DEVICEREMOVECOMPLETE && m_usb.IsAttached()) 
			|| ((int)wParam == DBT_DEVICEARRIVAL && !m_usb.IsAttached()) )
		{
			
			/*PDEV_BROADCAST_DEVICEINTERFACE pdev = (PDEV_BROADCAST_DEVICEINTERFACE)lParam;
			if(pdev->dbcc_devicetype == DBT_DEVTYP_DEVICEINTERFACE){
				CString dbcc = pdev->dbcc_name;
				dbcc.MakeUpper();
				dbcc.Find(_T("PROD_MASS_STORAGE&REV_0001\8&12D5BBDE&0"))*/
			
			bool wasAttached = m_usb.IsAttached();

			DWORD error = m_usb.attachDevice();
			if(!wasAttached && m_usb.IsAttached()){
				
				SetForegroundWindow(m_hWnd);

				SetWindowText(_T("USB Connected - Wireless Tag Manager"));
				EnableWindow(TRUE);			
				::SendMessage(m_hwndStatus, SB_SETTEXT, (255 ), (LPARAM)_T("Ready"));

				SetTimer(1, 10e3);
				SetTimer(2, 2000);

			}else if(!m_usb.IsAttached()){
				
				KillTimer(1);

				if(m_autoOpen){
					/*HANDLE hMutexOneInstance = ::CreateMutex( NULL, TRUE, g_LauncherMutexName); 
					bool launcher_running = (GetLastError() == ERROR_ALREADY_EXISTS);
					ReleaseMutex(hMutexOneInstance); 
					CloseHandle(hMutexOneInstance);	
			
					if(launcher_running){
					*/
						EndDialog(0);
						return 0;
					//}
				}

				SetWindowText(_T("USB Not Connected - Wireless Tag Manager"));
				EnableWindow(FALSE);

				if(error!=259){
					CString msg;
					LPVOID lpMsgBuf;
					FormatMessage(
						FORMAT_MESSAGE_ALLOCATE_BUFFER | 
						FORMAT_MESSAGE_FROM_SYSTEM,
						NULL,
						error,
						MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
						(LPTSTR) &lpMsgBuf,
						0, NULL );
					msg.Format(_T("Failed to open Tag Manager USB stick with error %d: %s"), error, lpMsgBuf); 
					LocalFree(lpMsgBuf);
					::SendMessage(m_hwndStatus, SB_SETTEXT, (255 ), (LPARAM)msg.GetString());
				}else{
					if(m_usb.m_anotherStickDetected)
						::SendMessage(m_hwndStatus, SB_SETTEXT, (255 ), (LPARAM)_T("The Tag Manager USB stick is currently in use. Please connect another Tag Manager USB stick."));
					else
						::SendMessage(m_hwndStatus, SB_SETTEXT, (255 ), (LPARAM)_T("Please connect a Tag Manager USB stick"));
				}

			}
		}
		return 0;
	}
	LRESULT OnResize(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
	{
		m_list.MoveWindow(7,48,LOWORD(lParam)-14, HIWORD(lParam)-75);
		SendMessage(m_hwndStatus, WM_SIZE, wParam, lParam);
		return 0;
	}
	BOOL m_InternetConnAvailable;

	// use timer to check alive should be set to false for each tag
	void OnTimer(UINT_PTR nIDEvent){
		if(nIDEvent >= 100){
			CSlaveProfile* profile;
			int nItem;
			if(m_list.GetSlaveFromId(nIDEvent-100, profile, nItem)){
				DoMotionSensorNotify(profile, nItem);
			}
			KillTimer(nIDEvent);
			return;
		}
		else if(nIDEvent == 3){
			DWORD flags;
			BOOL ret = InternetGetConnectedState(&flags, 0);
			if(!m_InternetConnAvailable && ret){
				FirstActionWhenInternetConnected();
			}else if(m_InternetConnAvailable && !ret && !m_dontConn){
				::PostMessage(m_hwndStatus, SB_SETTEXT, (255 ), (LPARAM)_T("Connection to mytaglist.com is lost. Please reconnect Internet."));
			}
			m_InternetConnAvailable=ret;
		}
		else if(nIDEvent == 1){
			if(m_secUpdateInterval!=-1)
				m_list.MarkSlaveDeadOlderThan(m_secUpdateInterval+180);		// 32 retry * 3.6
		}
		else if(nIDEvent==2){
			KillTimer(2);
			if(m_usb.IsAttached()){
				this->OnIntervalModified(0,0,0);

				UsbPacket_OUT packet;
				ZeroMemory(&packet, sizeof(packet));
				packet.cmd = CMD_ARM_MOTION_SENSOR;

				for(int i=0;i<m_list.m_aSlaves.GetSize();i++){
					CSlaveProfile& profile = m_list.m_aSlaves[i];
					if(profile.m_tagType == CSlaveProfile::tagHmcArmed ){
						profile.m_hmcOption.FillInPacket(&packet);
						packet.slave_id = profile.m_ID;
						sendCmd(packet, !profile.m_migrationPending);
						m_list.StartAnimate(i);
					}
				}

				if(m_list.AnyArmedOrBeepingTags() || (m_secUpdateInterval != -1))
					m_usb.StartListening();
			}
		}
		//m_list.FlushIni();
	}
	
	DWORD  m_secUpdateInterval;

	LRESULT OnIntervalModified(UINT uNotifyCode, int nID, CWindow wndCtl )
	{
		UsbPacket_OUT packet;
		ZeroMemory(&packet, sizeof(packet));
		packet.cmd = CMD_SET_SLAVE_POSTBACK_INTERVAL;
		
		Slave_Flash_Section flash;
		m_usb.fill_in_radio_related_config(flash,0);
		packet.extra.slave_postback_interval.count_unit = 1+floor(60e3/35/flash.Code_ACTIVE_INTERVAL); 
  
		switch(m_cbUpdateInterval.GetCurSel()){
			case 6:
				packet.extra.slave_postback_interval.count = 0;
				packet.extra.slave_postback_interval.count_unit = 0;
				sendCmdToAll(packet);
				m_secUpdateInterval=-1;
				break;
			case 0:
				packet.extra.slave_postback_interval.count = 1;
				sendCmdToAll(packet);

				m_secUpdateInterval=60;
				break;
			case 1:
				packet.extra.slave_postback_interval.count = 2;
				sendCmdToAll(packet);
				m_secUpdateInterval=2*60;
				break;
			case 2:
				packet.extra.slave_postback_interval.count = 5;
				sendCmdToAll(packet);
				m_secUpdateInterval=5*60;
				break;
			case 3:
				packet.extra.slave_postback_interval.count = 10;
				sendCmdToAll(packet);
				m_secUpdateInterval=10*60;
				break;
			case 4:
				packet.extra.slave_postback_interval.count = 30;
				sendCmdToAll(packet);
				m_secUpdateInterval=30*60;
				break;
			case 5:
				packet.extra.slave_postback_interval.count = 60;
				sendCmdToAll(packet);
				m_secUpdateInterval=60*60;
				break;
		}

		if(m_usb.IsAttached()){
			if(m_secUpdateInterval!=-1 && !m_usb.IsListening()){
				m_usb.StartListening();
			}
		}
		SaveIni();
		return 0;
	}
	
	void SaveIni(){
		CString data;
		data.Format(_T("%d"), m_usb.m_master_ids[0]);
		WritePrivateProfileString(_T("MasterID"), _T("ID0"), data, m_list.ini_fn);
		data.Format(_T("%d"), m_usb.m_master_ids[1]);
		WritePrivateProfileString(_T("MasterID"), _T("ID1"), data, m_list.ini_fn);
		data.Format(_T("%d"), m_usb.m_master_ids[2]);
		WritePrivateProfileString(_T("MasterID"), _T("ID2"), data, m_list.ini_fn);
		
		data.Format(_T("%d"), m_cbUpdateInterval.GetCurSel());
		WritePrivateProfileString(_T("Setting"), _T("IntervalSel"), data, m_list.ini_fn);
		
		data.Format(_T("%d"), this->m_beepDurationDefault);
		WritePrivateProfileString(_T("Setting"), _T("BeepDuration"), data, m_list.ini_fn);
		
		data.Format(_T("%d"), CCaoGadgetsDotCom::m_enableEmails);
		WritePrivateProfileString(_T("Setting"), _T("EnableEmails"), data, m_list.ini_fn);

		m_usb.m_newConfig.WriteIni(_T("NewConfig"), m_list.ini_fn);
		m_usb.m_oldConfig.WriteIni(_T("OldConfig"), m_list.ini_fn);
	}

	LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		m_usb.detachDevice();
		
		m_regKey.Close();
		SaveIni();		
		m_list.WriteFile();
		m_list.FlushIni();

		m_usb.PopNotificationWnd();

		CCaoGadgetsDotCom *web = new CCaoGadgetsDotCom();
		web->ManagerLogout(m_accEmail, m_accPwd, true, 1000);

		return 0;
	}
	
	CString m_accEmail, m_accPwd;
	bool m_dontConn;

	HWND m_hwndStatus;
	LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
	{

		m_hwndStatus = CreateStatusWindow(WS_CHILD| WS_VISIBLE|SBARS_SIZEGRIP, _T("Finding USB..."), m_hWnd, IDC_STATUSBAR);
		::SendMessage(m_hwndStatus, SB_SIMPLE, TRUE, 0L);				

		CCaoGadgetsDotCom::m_ircReceiver.SetNotificationWnd(m_hWnd, m_hwndStatus);

		CCaoGadgetsDotCom::m_hwndStatus = m_hwndStatus;
		
		// center the dialog on the screen
		CenterWindow();
		
		m_list.ReadFile();
		m_list.SubclassWindow( GetDlgItem( IDC_USERLIST ) );
		m_list.SetFocusSubItem( TRUE );

		m_usb.m_newConfig.ReadIni(_T("NewConfig"), m_list.ini_fn);
		m_usb.m_oldConfig.ReadIni(_T("OldConfig"), m_list.ini_fn);

		GUID newid;
		CoCreateGuid(&newid);
		m_usb.m_master_ids[0]=GetPrivateProfileInt(_T("MasterID"), _T("ID0"), newid.Data4[0], m_list.ini_fn);
		m_usb.m_master_ids[1]=GetPrivateProfileInt(_T("MasterID"), _T("ID1"), newid.Data4[1], m_list.ini_fn);
		m_usb.m_master_ids[2]=GetPrivateProfileInt(_T("MasterID"), _T("ID2"), newid.Data4[2], m_list.ini_fn);

		CMenu sysMenu = GetSystemMenu(FALSE);
		sysMenu.AppendMenu(MF_SEPARATOR);
		
		sysMenu.AppendMenu(MF_STRING, ID_FILE_NEW, _T("Create mytaglist.com &Account"));
		sysMenu.AppendMenu(MF_STRING, ID_FILE_OPEN, _T("Update mytaglist.com &Account"));
		sysMenu.AppendMenu(MF_SEPARATOR);
		
		sysMenu.AppendMenu(MF_STRING, ID_APP_ABOUT, _T("&Version Info"));
		sysMenu.AppendMenu(MF_STRING, ID_FILE_UPDATE, _T("Check &Update"));
		sysMenu.AppendMenu(MF_SEPARATOR);
		sysMenu.AppendMenu(MF_STRING, ID_FILE_SAVE, _T("Migrate &Wireless Configuration"));
		
		CCaoGadgetsDotCom::m_enableEmails = true;

		m_btnAutoOpen.Attach(GetDlgItem( IDC_CHECK_RECEIVE_EVENTS) );
		
		LONG result = m_regKey.Open(HKEY_CURRENT_USER, g_regkey);
		if(result != ERROR_SUCCESS)
		{
			result = m_regKey.Create(HKEY_CURRENT_USER, g_regkey);
		}
		if(result == ERROR_SUCCESS)
		{
			DWORD val;
			if(ERROR_SUCCESS != m_regKey.QueryDWORDValue(_T("autoOpen"), val))
			{
				m_autoOpen=false;
				m_btnAutoOpen.SetCheck(BST_UNCHECKED);
			}
			else{
				m_btnAutoOpen.SetCheck(val ? BST_CHECKED : BST_UNCHECKED);
				m_autoOpen=(val!=0);
			}
		}	
		
		m_dontConn = GetPrivateProfileInt(_T("Account"), _T("DontConn"), 0, m_list.ini_fn);
		
		DWORD flags;
		m_InternetConnAvailable=InternetGetConnectedState(&flags, 0);
		if(m_InternetConnAvailable){
			FirstActionWhenInternetConnected();
		}
		SetTimer(3, 2000, 0);

		/*if(GetPrivateProfileInt(_T("Setting"), _T("EnableEmails"), 1, m_list.ini_fn)){
			m_btnAutoOpen.SetCheck(BST_CHECKED);
			CCaoGadgetsDotCom::m_enableEmails = true;
		}
		else{
			m_btnAutoOpen.SetCheck(BST_UNCHECKED);
			CCaoGadgetsDotCom::m_enableEmails = false;	
		}
		*/

		m_cbUpdateInterval.Attach(GetDlgItem( IDC_COMBO_INTERVAL) );
		m_cbUpdateInterval.AddString(_T("1 Minute"));
		m_cbUpdateInterval.AddString(_T("2 Minute"));
		m_cbUpdateInterval.AddString(_T("5 Minute"));
		m_cbUpdateInterval.AddString(_T("10 Minute"));
		m_cbUpdateInterval.AddString(_T("30 Minute"));
		m_cbUpdateInterval.AddString(_T("1 Hour"));
		m_cbUpdateInterval.AddString(_T("Manual"));
		
		m_cbUpdateInterval.SetCurSel(GetPrivateProfileInt(_T("Setting"), _T("IntervalSel"), 6, m_list.ini_fn));
		//m_secUpdateInterval=-1;
		m_beepDurationDefault = GetPrivateProfileInt(_T("Setting"), _T("BeepDuration"), 115, m_list.ini_fn);

		// set icons
		HICON hIcon = (HICON)::LoadImage(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDR_MAINFRAME), 
			IMAGE_ICON, ::GetSystemMetrics(SM_CXICON), ::GetSystemMetrics(SM_CYICON), LR_DEFAULTCOLOR);
		SetIcon(hIcon, TRUE);
		HICON hIconSmall = (HICON)::LoadImage(_Module.GetResourceInstance(), MAKEINTRESOURCE(IDR_MAINFRAME), 
			IMAGE_ICON, ::GetSystemMetrics(SM_CXSMICON), ::GetSystemMetrics(SM_CYSMICON), LR_DEFAULTCOLOR);
		SetIcon(hIconSmall, FALSE);
	
		m_usb.PushNotificationWnd(m_hWnd);
		PostMessage(WM_DEVICECHANGE, DBT_CONFIGCHANGED, 0);
		
		SaveIni();
		
		CCaoGadgetsDotCom *web = new CCaoGadgetsDotCom();
		web->CheckUpdate(m_hWnd,false);

		return TRUE;
	}
	void FirstActionWhenInternetConnected(){
		if(!m_dontConn){
			if(!GetPrivateProfileString(_T("Account"), _T("Email"), _T(""), m_accEmail.GetBuffer(256), 255, m_list.ini_fn)){
				m_accEmail.ReleaseBuffer();
				CCreateAccountDialog dlg(m_accEmail, m_accPwd, &m_list.m_emailAddrs, m_dontConn);

				if(dlg.DoModal(m_hWnd))
				{
					WritePrivateProfileString(_T("Account"), _T("Email"), m_accEmail, m_list.ini_fn);
					WritePrivateProfileString(_T("Account"), _T("Password"), m_accPwd, m_list.ini_fn);
					WritePrivateProfileString(_T("Account"), _T("DontConn"), m_dontConn?_T("1"):_T("0"), m_list.ini_fn);
					m_list.FlushIni();
				}else{
					WritePrivateProfileString(_T("Account"), _T("DontConn"), m_dontConn?_T("1"):_T("0"), m_list.ini_fn);
					m_list.FlushIni();
					m_dontConn=true;   // but do not save to ini. 
				}
			}else{
				m_accEmail.ReleaseBuffer();
				GetPrivateProfileString(_T("Account"), _T("Password"), _T(""), m_accPwd.GetBuffer(256), 255, m_list.ini_fn);
				m_accPwd.ReleaseBuffer();
			}
		}

		if(!m_dontConn){
			CCaoGadgetsDotCom *web2 = new CCaoGadgetsDotCom();
			web2->ManagerLoginOrUpdateAll(true, m_accEmail, m_accPwd, m_list.m_aSlaves);
		}
		
	}
	bool m_timer_enabled;
	CWaitCursor m_waitCursor;
	
	void HandleCtxMenu(int cmdid, CSlaveProfile* profile=NULL, int nItem=0){
		UsbPacket_OUT packet;

		switch(cmdid){
			case 201:
				profile->m_migrationPending = !profile->m_migrationPending;
				m_list.InvalidateItem(nItem);
				break;

			case 8:
				{
					CBeeperOptionDialog optionDlg (profile, &m_list.m_emailAddrs);
					if(optionDlg.DoModal(m_hWnd)){
						if(optionDlg.m_applyToAll){
							m_list.ApplyBeeperOptionToAll(profile->m_beeperOption);
						}
					}
				}
				break;
			case 17:
				m_beepDurationDefault=profile->m_beepDurationDefault=cmdid;
				ZeroMemory(&packet, sizeof(packet));
				packet.slave_id = profile->m_ID;
				packet.cmd = CMD_BEEP_UNTIL_MOVED;
				profile->m_hmcOption.FillInPacket(&packet);
				sendCmd(packet, !profile->m_migrationPending);
				m_list.StartAnimate(nItem);
				break;

			case 12:
				m_beepDurationDefault=profile->m_beepDurationDefault=cmdid;
				ZeroMemory(&packet, sizeof(packet));
				packet.slave_id = profile->m_ID;
				packet.cmd = CMD_BEEP;
				sendCmd(packet, !profile->m_migrationPending);
				m_list.StartAnimate(nItem);
				break;

			case 105:
			case 110:
			case 115:
			case 120:
			case 130:
				m_beepDurationDefault=profile->m_beepDurationDefault=cmdid;
				ZeroMemory(&packet, sizeof(packet));
				packet.slave_id = profile->m_ID;
				packet.cmd = CMD_BEEP_FOR_DURATION;
				packet.extra.beep_for_duration.beep_duration = m_beepDurationDefault-100;
				sendCmd(packet, !profile->m_migrationPending);
				m_list.StartAnimate(nItem);
				break;

			case 13:
				ZeroMemory(&packet, sizeof(packet));
				packet.slave_id = profile->m_ID;
				packet.cmd = CMD_BEEP_STOP;
				sendCmd(packet, !profile->m_migrationPending);
				m_list.StartAnimate(nItem);
				break;
			case 16:
				ZeroMemory(&packet, sizeof(packet));
				packet.cmd = CMD_BEEP_STOP;
				sendCmdToAll(packet);
				break;
			case 7:
				if(profile->m_hmcOption.beep_tag){
					ZeroMemory(&packet, sizeof(packet));
					packet.slave_id = profile->m_ID;
					packet.cmd = CMD_BEEP_STOP;
					
					sendCmd(packet, !profile->m_migrationPending);
				}
				profile->m_fired=false;
				PlaySound(NULL, NULL, SND_ASYNC);

				if(!m_dontConn){
					CCaoGadgetsDotCom* web = new CCaoGadgetsDotCom();
					web->ManagerUpdate(m_accEmail, m_accPwd, *profile, CMD_WEB_RESET);

					/*if(CCaoGadgetsDotCom::m_ircReceiver.SendReply(profile->m_ID, CMD_WEB_RESET, _T("AsyncUpdate"))){
						::PostMessage(m_hwndStatus, SB_SETTEXT, (255 ), (LPARAM)_T("Reset a tag via command from mytaglist.com"));
					}*/

				}
				
				m_list.InvalidateItem(nItem);
				break;
			case 6:
				{
					CHmcOptionDialog optionDlg (profile, &m_list.m_emailAddrs);
					if(optionDlg.DoModal(m_hWnd)){
						if(optionDlg.m_applyToAll){
							m_list.ApplyHmcOptionToAll(profile->m_hmcOption);
						}
						if(optionDlg.m_needUpdateTag){
							ZeroMemory(&packet, sizeof(packet));
							packet.slave_id = profile->m_ID;
							//packet.cmd = profile->m_hmcOption.beep_tag ? CMD_ARM_MOTION_SENSOR_BEEP_UPON_MOVE : CMD_ARM_MOTION_SENSOR;
							packet.cmd = CMD_ARM_MOTION_SENSOR;
							profile->m_hmcOption.FillInPacket(&packet);
							if(optionDlg.m_applyToAll)
								sendCmdToList(packet, m_list.getAllArmedTags());
							else{
								sendCmd(packet, !profile->m_migrationPending);
								m_list.StartAnimate(nItem);
							}
						}
					}
				}
				break;
			case 4:
			case 14:
				ZeroMemory(&packet, sizeof(packet));
				//packet.cmd = profile->m_hmcOption.beep_tag ? CMD_ARM_MOTION_SENSOR_BEEP_UPON_MOVE : CMD_ARM_MOTION_SENSOR;
				packet.cmd = CMD_ARM_MOTION_SENSOR;
				if(cmdid==14){

					for(int i=0;i<m_list.m_aSlaves.GetSize();i++){
						CSlaveProfile& p = m_list.m_aSlaves[i];
						if(p.m_tagType == CSlaveProfile::tagHmc ||  p.m_tagType == CSlaveProfile::tagHmcArmed){
							p.m_hmcOption.FillInPacket(&packet);
							break;
						}
					}

					sendCmdToAll(packet);
				}else{
					profile->m_hmcOption.FillInPacket(&packet);
					packet.slave_id = profile->m_ID;
					sendCmd(packet, !profile->m_migrationPending);
					m_list.StartAnimate(nItem);
				}
				break;
			case 5:
			case 15:

				PlaySound(NULL, NULL, SND_ASYNC);
				ZeroMemory(&packet, sizeof(packet));
				packet.cmd = CMD_DISARM_MOTION_SENSOR;
				
				if(cmdid==15){
					sendCmdToAll(packet);
				}else{
					packet.slave_id = profile->m_ID;
					sendCmd(packet, !profile->m_migrationPending);
					m_list.StartAnimate(nItem);
				}
				break;
			case 2:
				m_list.EditItem(nItem,CListSlaveData::colName);
				break;
			case 3:
				m_list.EditItem(nItem,CListSlaveData::colComment);
				break;
			case 1:
				if(IDOK==MessageBox(_T("Click OK to program this tag to its factory original state so it can be associated with another Wireless Tag Manager. "), 
					_T("Confirmation"), MB_OKCANCEL|MB_ICONINFORMATION)){

						m_waitCursor.Set();
						
						Slave_Flash_Section flash;
						ZeroMemory(&flash, sizeof(Slave_Flash_Section));
						m_usb.fill_in_new_slave_config(flash);

						FILETIME filetime;
						GetSystemTimeAsFileTime(&filetime);
						memcpy(flash.Code_Master_Signature, &filetime, sizeof(filetime));

						m_usb.set_cmd_timeout_ms(9000);
						m_usb.turn_on_radio(!profile->m_migrationPending);
						UsbPacket_OUT packet;	
						ZeroMemory(&packet, sizeof(packet));
						packet.slave_id = profile->m_ID;
						packet.cmd = CMD_WRITE_SLAVE_FLASH;
						packet.extra.flash_to_write = flash;

						m_usb.SendPacketAsync(&packet);
						m_list.StartAnimate(nItem);
						//m_usb.turn_off_radio();   //turned off at event handler
				}
				break;
		}
	}

	int m_beepDurationDefault;
	
	LRESULT DisplayContextMenu(int nItem, int x, int y, CListSlaveData::Columns type)
	{
		CSlaveProfile *profile;	
		if(!m_list.GetSlave(nItem, profile))
			return 0;
				
		CMenu ctxMenu, ctxBeepMenu;
		ctxMenu.CreatePopupMenu();
		ctxBeepMenu.CreatePopupMenu();

		int bdd = profile->m_beepDurationDefault ? profile->m_beepDurationDefault : m_beepDurationDefault;

		if(profile->m_tagType == CSlaveProfile::tagHmc || profile->m_tagType == CSlaveProfile::tagHmcArmed){
			ctxBeepMenu.AppendMenu(MF_STRING|(bdd==17?MF_CHECKED:MF_UNCHECKED), 17, _T("Until &Moved"));
			ctxBeepMenu.AppendMenu(MF_SEPARATOR);
		}
		ctxBeepMenu.AppendMenu(MF_STRING|(bdd==105?MF_CHECKED:MF_UNCHECKED), 105, _T("For &5 seconds"));
		ctxBeepMenu.AppendMenu(MF_STRING|(bdd==110?MF_CHECKED:MF_UNCHECKED), 110, _T("For 1&0 seconds"));
		ctxBeepMenu.AppendMenu(MF_STRING|(bdd==115?MF_CHECKED:MF_UNCHECKED), 115, _T("For &15 seconds"));
		ctxBeepMenu.AppendMenu(MF_STRING|(bdd==120?MF_CHECKED:MF_UNCHECKED), 120, _T("For &20 seconds"));
		ctxBeepMenu.AppendMenu(MF_STRING|(bdd==130?MF_CHECKED:MF_UNCHECKED), 130, _T("For &30 seconds"));
		ctxBeepMenu.AppendMenu(MF_STRING|(bdd==12?MF_CHECKED:MF_UNCHECKED), 12, _T("Until &Stop Command"));

		ctxMenu.AppendMenu(MF_POPUP, ctxBeepMenu.m_hMenu, _T("&Beep ..."));
		ctxMenu.AppendMenu(MF_STRING, 13, _T("&Stop Beep"));
		ctxMenu.AppendMenu(MF_STRING, 16, _T("Stop All Beeps"));
		ctxMenu.AppendMenu(MF_SEPARATOR);		

		if(type == CListSlaveData::Columns::colName || type == CListSlaveData::Columns::colComment){
			ctxMenu.AppendMenu(MF_STRING, 2, _T("Change &Name"));
			ctxMenu.AppendMenu(MF_STRING, 3, _T("Edit &Comment"));
			ctxMenu.AppendMenu(MF_SEPARATOR);		
		}

		ctxMenu.AppendMenu(MF_STRING|(profile->m_Alive? 0 : MF_GRAYED), 4, _T("&Arm Motion Sensor"));	
		ctxMenu.AppendMenu(MF_STRING, 14, _T("A&rm All Motion Sensors"));	
		ctxMenu.AppendMenu(MF_SEPARATOR);		
		ctxMenu.AppendMenu(MF_STRING|(profile->m_Alive? 0 : MF_GRAYED), 5, _T("&Disarm Motion Sensor"));
		ctxMenu.AppendMenu(MF_STRING, 15, _T("D&isarm All Motion Sensors"));	
		ctxMenu.AppendMenu(MF_SEPARATOR);		
		ctxMenu.AppendMenu(MF_STRING, 6, _T("&Motion Sensor Tag Options ..."));	
		ctxMenu.AppendMenu(MF_SEPARATOR);		
		
		if(profile->m_fired && !profile->m_hmcOption.door_mode){
			ctxMenu.AppendMenu(MF_STRING, 7, _T("&Reset"));
		}
		ctxMenu.AppendMenu(MF_STRING, 8, _T("Basic Tag &Options ..."));	
		ctxMenu.AppendMenu(MF_SEPARATOR);
		
		if(profile->m_migrationPending){
			ctxMenu.AppendMenu(MF_STRING, 201, _T("Use New &Wireless Configuration"));
		}else{
			ctxMenu.AppendMenu(MF_STRING, 201, _T("Use Old &Wireless Configuration"));
		}

		//ctxMenu.AppendMenu(MF_STRING|(profile->m_Alive? 0 : MF_GRAYED), 1, _T("&Unassociate"));
		ctxMenu.AppendMenu(MF_STRING, 1, _T("&Unassociate"));

		//ctxMenu.AppendMenu(MF_STRING, 2, _T("Hide"));
		
		//SetForegroundWindow(m_hWnd);
		MSG msg;
		while(PeekMessage(&msg, m_hWnd, 0,0,PM_REMOVE)){
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
		
		int cmdid = ctxMenu.TrackPopupMenu(TPM_LEFTALIGN|TPM_RETURNCMD|TPM_NONOTIFY, x,y, m_hWnd);
		HandleCtxMenu(cmdid, profile, nItem);
		return 0;
	}

	LRESULT OnUserListRightClick( LPNMHDR lpNMHDR )
	{
		CListNotify *pn = reinterpret_cast<CListNotify *>( lpNMHDR );
		if(pn->m_nItem==NULL_ITEM)
			return 0;
		POINT cursorPos;
		::GetCursorPos(&cursorPos);
		return DisplayContextMenu(pn->m_nItem, cursorPos.x, cursorPos.y, (CListSlaveData::Columns)pn->m_nSubItem);
	}
	LRESULT OnUserListLeftClick( LPNMHDR lpNMHDR )
	{
		CListNotify *pn = reinterpret_cast<CListNotify *>( lpNMHDR );
		if(pn->m_nItem==NULL_ITEM)
			return 0;
		
		if( (CListSlaveData::Columns)pn->m_nSubItem == CListSlaveData::Columns::colBat ||
			(CListSlaveData::Columns)pn->m_nSubItem == CListSlaveData::Columns::colSignalStrength ||
			(CListSlaveData::Columns)pn->m_nSubItem == CListSlaveData::Columns::colAge){
			return OnUserListRightClick(lpNMHDR);
		}
		if((CListSlaveData::Columns)pn->m_nSubItem == CListSlaveData::Columns::colBeep){
			
			if(!m_list.IsAnimating(pn->m_nItem)){
				
				CSlaveProfile *profile;
					
				m_list.GetSlave(pn->m_nItem, profile);
				
				UsbPacket_OUT packet;
		
				ZeroMemory(&packet, sizeof(packet));
				packet.slave_id = profile->m_ID;
				
				if(profile->m_migrationPending){
					Slave_Flash_Section flash;
					ZeroMemory(&flash, sizeof(Slave_Flash_Section));
				
					flash.Code_Master_ID_1 = m_usb.m_master_ids[0];
					flash.Code_Master_ID_2 = m_usb.m_master_ids[1];
					m_usb.fill_in_radio_related_config(flash, m_usb.m_master_ids[2]);
					//m_usb.m_newConfig.update_slave_flash(flash);

					FILETIME filetime;
					GetSystemTimeAsFileTime(&filetime);
					memcpy(flash.Code_Master_Signature, &filetime, sizeof(filetime));
					
					flash.Code_Slave_ID = profile->m_ID;

					packet.cmd = CMD_MIGRATE_SLAVE_FLASH;
					packet.extra.flash_to_write = flash;

					bool wasListening = m_usb.IsListening();
					if(wasListening){
						m_usb.StopListening();
					}

					m_usb.set_cmd_timeout(false);
					m_usb.turn_on_radio(false);
					m_usb.SendPacketAsync(&packet);
					m_usb.turn_off_radio();
					
					if(wasListening){
						m_usb.StartListening();
					}
					
					m_list.StartAnimate(pn->m_nItem);						
				}
				else if(profile->m_Beeping)
				{
					packet.cmd = CMD_BEEP_STOP;
					this->sendCmd(packet, !profile->m_migrationPending);
					m_list.StartAnimate(pn->m_nItem);
				}
				else if(!profile->m_fired || profile->m_hmcOption.door_mode)
				{
					int bdd = profile->m_beepDurationDefault ? profile->m_beepDurationDefault : m_beepDurationDefault;
					if(bdd==17 && profile->m_tagType == CSlaveProfile::tagHmc || profile->m_tagType == CSlaveProfile::tagHmcArmed){
						// do according to m_beepDurationDefault !!
						packet.cmd = CMD_BEEP_UNTIL_MOVED;
						profile->m_hmcOption.FillInPacket(&packet);
					}else{
						if(bdd<100){
							packet.cmd = CMD_BEEP;
						}else{
							packet.cmd = CMD_BEEP_FOR_DURATION;
							packet.extra.beep_for_duration.beep_duration = bdd-100;
						}
					}
					this->sendCmd(packet, !profile->m_migrationPending);
					m_list.StartAnimate(pn->m_nItem);
				}

				if(profile->m_fired && !profile->m_hmcOption.door_mode){
					profile->m_fired=false;
					PlaySound(NULL, NULL, SND_ASYNC);

					m_list.InvalidateItem(pn->m_nItem);			
					
					if(!m_dontConn){
						CCaoGadgetsDotCom* web = new CCaoGadgetsDotCom();
						web->ManagerUpdate(m_accEmail, m_accPwd, *profile, CMD_LISTEN_FOR_ONE_EVENT);
					}

				}
			}
		}
		return 0;
	}
	
	LRESULT OnUserModified( LPNMHDR lpNMHDR )
	{
		CListNotify *pn = reinterpret_cast<CListNotify *>( lpNMHDR );
		if(pn->m_nItem==NULL_ITEM)
			return 0;

		CSlaveProfile *profile=NULL;			
		m_list.GetSlave(pn->m_nItem, profile);
		
		if(!m_dontConn && profile){
			CCaoGadgetsDotCom* web = new CCaoGadgetsDotCom();
			web->ManagerUpdate(m_accEmail, m_accPwd, *profile, CMD_LISTEN_FOR_ONE_EVENT);
		}
		return 0;
	}
	LRESULT OnUserListSpaceKey( LPNMHDR lpNMHDR )
	{
		CListNotify *pn = reinterpret_cast<CListNotify *>( lpNMHDR );
		if(pn->m_nItem==NULL_ITEM)
			return 0;
		
		if((CListSlaveData::Columns)pn->m_nSubItem != CListSlaveData::Columns::colBeep)
		{
			CRect rect;
			m_list.GetItemRect(pn->m_nItem, pn->m_nSubItem, rect);
			POINT p; p.x=rect.left; p.y=rect.bottom;
			::ClientToScreen(m_list.m_hWnd, &p);
			return DisplayContextMenu(pn->m_nItem, p.x, p.y, (CListSlaveData::Columns)pn->m_nSubItem );
		}
		else return OnUserListLeftClick(lpNMHDR);
	}	

	int m_outstandingQuery;
	
	void sendCmd(UsbPacket_OUT& packet, bool newConfig)
	{
		m_usb.set_cmd_timeout(newConfig);
		if(!m_usb.IsListening())
			m_usb.turn_on_radio(newConfig);
		m_usb.SendPacketAsync(&packet);
		if(!m_usb.IsListening())
			m_usb.turn_off_radio();
	}

	void sendCmdToList(UsbPacket_OUT& packet, std::map<int,int> idlist)
	{
		if(idlist.empty())return;
		if(!m_usb.IsAttached())return;
		
		m_usb.set_cmd_timeout(true);
		packet.cmd |= CMD_MULTIPLE_MASK;
		packet.slave_id_begin_iterate = 0xff;
		packet.slave_id_begin =0xff;
		packet.slave_id_end =  0;

		std::map<int,int>::iterator i;
		for(i=idlist.begin(); i!=idlist.end(); i++){
			if(packet.slave_id_begin >i->second)packet.slave_id_begin=i->second;
		}
		for(i=idlist.begin(); i!=idlist.end(); i++){
			if(packet.slave_id_end < i->second)packet.slave_id_end=i->second;
		}
		
		if(!m_usb.IsListening())
			m_usb.turn_on_radio(true);		

		int b=0;
		i = idlist.begin();
		while(i != idlist.end()){
			if(b==CMD_MULTI_ONCE){
				packet.num_expected_replies = b;
				MyTrace(_T("USB Packet OUT LIST cmd = 0x%x, b=%d\n"),  packet.cmd, b);
				m_usb.SendPacketAsync(&packet);
				b=0;
			}
			m_list.StartAnimate(i->first);
			packet.extra.general.target_ids[b++]=i->second;
			i++;
		}
		
		packet.num_expected_replies = b;
		MyTrace(_T("USB Packet OUT LIST cmd = 0x%x, b=%d\n"),  packet.cmd, b);
		m_cmdMultiplePacket.push_back(packet);
		m_usb.SendPacketAsync(&packet);

		if(!m_usb.IsListening())
			m_usb.turn_off_radio();
	}

	std::list<UsbPacket_OUT> m_cmdMultiplePacket;

	void sendCmdToAll(UsbPacket_OUT& packet)
	{
//		sendCmdToList(packet, m_list.getAllTags());
		if(m_list.GetItemCount()==0)return;
		if(!m_usb.IsAttached())return;

		m_usb.set_cmd_timeout(true);
		
		m_list.StartAnimateAll();
		if(!m_usb.IsListening())
			m_usb.turn_on_radio(true);
		
		packet.cmd |= CMD_MULTIPLE_MASK;
		int min_id = m_list.GetMinimumID();
		int max_id = m_list.GetMaximumID();

		packet.slave_id_begin = packet.slave_id_begin_iterate = min_id;

		while(1){
			m_outstandingQuery++;
			
			packet.slave_id_end_iterate = packet.slave_id_begin_iterate+CMD_MULTI_ONCE-1;

			if(packet.slave_id_end_iterate > max_id){
				packet.slave_id_end_iterate = max_id;
			}
			packet.slave_id_end = packet.slave_id_end_iterate;

			packet.num_expected_replies = m_list.GetNumSlaveWithIDRange(packet.slave_id_begin_iterate, packet.slave_id_end_iterate);

			MyTrace(_T("USB Packet OUT MULTI cmd = 0x%x, id_begin=%d, id_end=%d\n"),  
						packet.cmd, packet.slave_id_begin_iterate, packet.slave_id_end_iterate);

			m_cmdMultiplePacket.push_back(packet);

			m_usb.SendPacketAsync(&packet);
			
			if(packet.slave_id_end_iterate == max_id)
				break;

			packet.slave_id_begin = packet.slave_id_begin_iterate = packet.slave_id_end_iterate+1;
		}
				
		if(!m_usb.IsListening())
			m_usb.turn_off_radio();

	}

	LRESULT OnQuitForUpdate(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
//		MessageBox("quit for update");
		EndDialog(IDC_QUIT_FOR_UPDATE);
		return 0;
	}

	LRESULT OnUpdateStat(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		UsbPacket_OUT packet;
		ZeroMemory(&packet, sizeof(packet));
		packet.cmd = CMD_QUERY;
		sendCmdToAll(packet);
		return 0;
	}
	
	ATL::CRegKey m_regKey;
	bool m_autoOpen;

	void SetAutoOpen(bool bAutoOpen){
		if(bAutoOpen){
			m_autoOpen=true;
			m_regKey.SetDWORDValue(_T("autoOpen"), 1);
			m_regKey.Flush();

			bool AlreadyRunning; 
			HANDLE hMutexOneInstance = ::CreateMutex( NULL, TRUE, g_LauncherMutexName); 
			AlreadyRunning = (GetLastError() == ERROR_ALREADY_EXISTS); 
			if (hMutexOneInstance != NULL) 
			{ 
				::ReleaseMutex(hMutexOneInstance); 
				CloseHandle(hMutexOneInstance);
			} 

			CString installFolder;
			SHGetSpecialFolderPath(m_hWnd, installFolder.GetBuffer(MAX_PATH+1), CSIDL_LOCAL_APPDATA, TRUE);
			installFolder.ReleaseBuffer();
			installFolder.TrimRight(_T('\\'));
			installFolder.Append(_T("\\CaoGadgets\\"));

			CString thisExe; 
			GetModuleFileName(NULL, thisExe.GetBuffer(1024),1019);
			thisExe.ReleaseBuffer();
			CString installed_file = installFolder;
			installed_file.Append(thisExe.Right(thisExe.GetLength() - thisExe.ReverseFind(_T('\\')) - 1 ));

			if ( !AlreadyRunning ){
				
				if(!PathFileExists(installed_file)){
					CreateDirectory(installFolder, NULL);
				}				
				
				DeleteFile(installed_file);
				CopyFile(thisExe, installed_file, FALSE);

					/*SHFILEOPSTRUCT sp;
					ZeroMemory(&sp, sizeof(sp));
					sp.wFunc=FO_COPY;
					
					thisExe.AppendChar(0);
					sp.pFrom=thisExe;					
					
					installFolder.AppendChar(0);
					sp.pTo=installFolder;
					sp.fFlags=FOF_NOCONFIRMATION|FOF_SIMPLEPROGRESS|FOF_NOCONFIRMMKDIR;
					sp.lpszProgressTitle=_T("Installing WirelessTagManager.exe ...");
					SHFileOperation(&sp);
					*/
				

				STARTUPINFO si;
				PROCESS_INFORMATION pi;
				ZeroMemory( &si, sizeof(si) );
				si.cb = sizeof(si);
				si.dwFlags = STARTF_USESHOWWINDOW;
				si.wShowWindow = SW_HIDE;
				ZeroMemory( &pi, sizeof(pi) );
				CString cmdline; cmdline.Format(_T("\"%s\" /Launcher"), installed_file);
				CreateProcess(installed_file, cmdline.GetBuffer(), NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
				CloseHandle( pi.hProcess );
				CloseHandle( pi.hThread );
			}

			ATL::CRegKey regkey; regkey.Open(HKEY_CURRENT_USER, g_autoRunKey);
			CString cmdline;
			cmdline.Format(_T("\"%s\" /Launcher"), installed_file);
			regkey.SetStringValue(g_autoRunValue, cmdline);
			regkey.Close();

		}else{
			m_autoOpen=false;
			m_regKey.SetDWORDValue(_T("autoOpen"), 0);
			m_regKey.Flush();

			ATL::CRegKey regkey; regkey.Open(HKEY_CURRENT_USER, g_autoRunKey);
			regkey.DeleteValue(g_autoRunValue);
			regkey.Close();
		}
	}

	LRESULT OnCheckReceiveEvents(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		SetAutoOpen(m_btnAutoOpen.GetCheck() == BST_CHECKED);
		SetForegroundWindow(m_hWnd);

		//CCaoGadgetsDotCom::m_enableEmails = (m_btnAutoOpen.GetCheck() == BST_CHECKED);

		//CCaoGadgetsDotCom *web = new CCaoGadgetsDotCom();
		//web->SendMotionSensorNotifyEmail(_T("zhiheng@broadcom.com"));

/*		if(m_btnAutoOpen.GetCheck() == BST_CHECKED){
			m_usb.turn_off_radio();
			m_usb.start_ramp_orange_LED();
		}else{
			m_usb.stop_ramp_orange_LED();
		}
*/		return 0;
	}
/*
	void updateSlaveFlash(int nItem){

		CSlaveProfile *profile;			
		m_list.GetSlave(nItem, profile);
				
		Slave_Flash_Section flash;
		ZeroMemory(&flash, sizeof(Slave_Flash_Section));
		flash.Code_Slave_ID = profile->m_ID;
		flash.Code_Master_ID_1 = m_usb.m_master_ids[0];
		flash.Code_Master_ID_2 = m_usb.m_master_ids[1];
		m_usb.fill_in_radio_related_config(flash, m_usb.m_master_ids[2]);
		SYSTEMTIME systime;
		::GetSystemTime(&systime);
		FILETIME filetime;
		SystemTimeToFileTime(&systime, &filetime);
		memcpy(flash.Code_Master_Signature, &filetime, sizeof(filetime));
		
		UsbPacket_OUT packet;	
		ZeroMemory(&packet, sizeof(packet));
		packet.slave_id = profile->m_ID;
		packet.cmd = CMD_WRITE_SLAVE_FLASH;
		packet.extra.flash_to_write = flash; 
		
		sendCmd(packet);		
		m_list.StartAnimate(nItem);

	}
*/
	LRESULT OnAddNewSlave(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		Slave_Flash_Section flash;
		ZeroMemory(&flash, sizeof(Slave_Flash_Section));
		flash.Code_Slave_ID = m_list.AssignNewID();
		if(flash.Code_Slave_ID == 0xff){
			MessageBox(_T("One Wireless Tag Manager USB Stick can only handle up to 254 tags."),_T("Error"), MB_OK|MB_ICONWARNING);
			return 0;
		}
		
		flash.Code_Master_ID_1 = m_usb.m_master_ids[0];
		flash.Code_Master_ID_2 = m_usb.m_master_ids[1];

		m_usb.fill_in_radio_related_config(flash, m_usb.m_master_ids[2]);
		//m_usb.m_newConfig.update_slave_flash(flash);

		FILETIME filetime;
		GetSystemTimeAsFileTime(&filetime);
		memcpy(flash.Code_Master_Signature, &filetime, sizeof(filetime));

		bool autoOpenOrg = m_autoOpen;
		if(autoOpenOrg){
			SetAutoOpen(false);
		}
		bool wasListening = m_usb.IsListening();
		if(wasListening){
			m_usb.StopListening();
		}

		CNewWizardSheet wizard(&m_usb, flash, 0, m_hWnd);
		INT_PTR result = wizard.DoModal();
		if(result == IDOK && wizard.m_pageFinding.m_detected_slaves.GetSize()!=0)
		{
			CSlaveProfile profile(true, wizard.m_pageNaming.m_name, wizard.m_pageNaming.m_comment, 
					wizard.m_pageFinding.m_detected_slaves[0].m_batADCout, wizard.m_pageFinding.m_detected_slaves[0].m_arssiADCout, 
					flash.Code_Slave_ID, wizard.m_pageFinding.m_detected_slaves[0].m_tagType);
			
			m_list.SelectItem(m_list.m_aSlaves.Add(profile));
			if(this->m_secUpdateInterval!=-1){
				UsbPacket_OUT packet;
				ZeroMemory(&packet, sizeof(packet));
				
				packet.slave_id = profile.m_ID;
				packet.cmd = CMD_SET_SLAVE_POSTBACK_INTERVAL;
				Slave_Flash_Section flash;
				m_usb.fill_in_radio_related_config(flash,0);
				packet.extra.slave_postback_interval.count_unit = 1+floor(60e3/35/flash.Code_ACTIVE_INTERVAL); 
				packet.extra.slave_postback_interval.count = m_secUpdateInterval/60;
				sendCmd(packet, true);
			}

			for(int i=0;i<m_list.GetColumnCount();i++){
				m_list.AutoSizeColumn(i);
			}
			m_list.Invalidate();
			SaveIni();
			m_list.WriteFile();
			m_list.FlushIni();

			if(!m_dontConn){
				CCaoGadgetsDotCom* web = new CCaoGadgetsDotCom();
				web->ManagerUpdate(m_accEmail, m_accPwd, profile, CMD_LISTEN_FOR_ONE_EVENT);
			}
		}

		if(autoOpenOrg){
			SetAutoOpen(true);
		}
		if(wasListening){
			m_usb.StartListening();
		}


		return 0;
	}
	LRESULT OnOK(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		EndDialog(0);
		return 0;
	}

	LRESULT OnCancel(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
	{
		EndDialog(0);
		return 0;
	}
	LRESULT OnSysCmd(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		bHandled=FALSE;
		if(wParam == ID_FILE_SAVE){

			if(!m_usb.IsAttached()){
				MessageBox(_T("Please connect the USB stick"), _T("Wireless Tag Manager"), MB_OK|MB_ICONWARNING);
				return 0;
			}

			CString retry;
			
			for(int i=0;i<m_list.m_aSlaves.GetSize();i++){
				if(m_list.m_aSlaves[i].m_migrationPending){
					
					if(!retry.IsEmpty())					
						retry.Append(_T(","));

					retry.Append(m_list.m_aSlaves[i].m_Name);
				}
			}

			CMigrationDialog dlg(m_usb.m_newConfig, m_usb.m_oldConfig, retry);

			if(dlg.DoModal(m_hWnd) && m_usb.IsAttached()){

				Slave_Flash_Section flash;
				ZeroMemory(&flash, sizeof(Slave_Flash_Section));

				if(retry.IsEmpty()){
					m_usb.m_oldConfig = m_usb.m_newConfig;
					m_usb.m_newConfig = dlg.m_newConfig;
					this->SaveIni();
				}

				flash.Code_Master_ID_1 = m_usb.m_master_ids[0];
				flash.Code_Master_ID_2 = m_usb.m_master_ids[1];
				m_usb.fill_in_radio_related_config(flash, m_usb.m_master_ids[2]);
				//dlg.m_newConfig.update_slave_flash(flash);

				FILETIME filetime;
				GetSystemTimeAsFileTime(&filetime);
				memcpy(flash.Code_Master_Signature, &filetime, sizeof(filetime));
					
				bool wasListening = m_usb.IsListening();
				if(wasListening){
					m_usb.StopListening();
				}
				m_usb.set_cmd_timeout(false);
				m_usb.turn_on_radio(false);
				for(int i=0;i<m_list.m_aSlaves.GetSize();i++){
					
					
					CSlaveProfile* p = &m_list.m_aSlaves[i];
					if(!retry.IsEmpty() && !p->m_migrationPending)
						continue;

					flash.Code_Slave_ID = p->m_ID;					

					UsbPacket_OUT packet;	
					ZeroMemory(&packet, sizeof(packet));
					packet.slave_id = p->m_ID;
					packet.cmd = CMD_MIGRATE_SLAVE_FLASH;
					packet.extra.flash_to_write = flash;

					m_usb.SendPacketAsync(&packet);
					m_list.StartAnimate(i);
				}
				m_usb.turn_off_radio();

				if(wasListening){
					m_usb.StartListening();
				}

				if(retry.IsEmpty())
					m_list.FlushIni();
			}
		}
		if(wParam == ID_FILE_OPEN || wParam == ID_FILE_NEW)
		{
			int ret=0;
			if(wParam == ID_FILE_OPEN){
				CCreateAccountDialog dlg(m_accEmail, m_accPwd, &m_list.m_emailAddrs, m_dontConn);
				ret = dlg.DoModal(m_hWnd);
			}else{
				CString newEmail, newPwd;
				CCreateAccountDialog dlg(newEmail, newPwd, &m_list.m_emailAddrs, m_dontConn);
				if(ret = dlg.DoModal(m_hWnd)){
					m_accEmail=newEmail;
					m_accPwd=newPwd;
				}
			}
			if(ret){
				WritePrivateProfileString(_T("Account"), _T("Email"), m_accEmail, m_list.ini_fn);
				WritePrivateProfileString(_T("Account"), _T("Password"), m_accPwd, m_list.ini_fn);
				WritePrivateProfileString(_T("Account"), _T("DontConn"), m_dontConn?_T("1"):_T("0"), m_list.ini_fn);
				m_list.FlushIni();
				if(m_dontConn){
					CCaoGadgetsDotCom *web = new CCaoGadgetsDotCom();
					web->ManagerLogout(m_accEmail, m_accPwd);
				}else{
					CCaoGadgetsDotCom *web = new CCaoGadgetsDotCom();
					web->ManagerLoginOrUpdateAll(true, m_accEmail, m_accPwd, m_list.m_aSlaves);
				}
			}
		}
		else if(wParam == ID_APP_ABOUT)
		{
			CAboutDialog dlg;			
			dlg.DoModal();
			bHandled=TRUE;
		}else if(ID_FILE_UPDATE == wParam){
			CCaoGadgetsDotCom *web = new CCaoGadgetsDotCom();
			web->CheckUpdate(m_hWnd,true);			
		}
		return 0;
	}
};

